diff --git a/common/baseroom/baseRoom.go b/common/baseroom/baseRoom.go index 59dd377..3843cd5 100644 --- a/common/baseroom/baseRoom.go +++ b/common/baseroom/baseRoom.go @@ -3,34 +3,37 @@ package baseroom import ( "fmt" "game/common/proto/pb" + "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" "time" ) type BaseRoom[Seat ISeat] struct { - id int - roomType int // 房间配置id 初级,中级,高级 - playType int // 玩法配置id color玩法id - gameNo string - Seats []Seat - timeTypes map[TimerType]uint32 + id int + roomType int // 房间配置id 初级,中级,高级 + playType int // 玩法配置id color玩法id + gameNo string + Seats []Seat + processor *processor.Processor + timeProcessor *processor.Processor - subRoom IRoom - sTimer ITimer - sender ISender + timeTypes map[TimerType]uint32 + srv service.IService } -func NewBaseRoom[Seat ISeat](id, roomType, playType int, subRoom IRoom, sTimer ITimer, sender ISender) (*BaseRoom[Seat], pb.ErrCode) { +func NewBaseRoom[Seat ISeat](id, roomType, playType int, srv service.IService) (*BaseRoom[Seat], pb.ErrCode) { room := &BaseRoom[Seat]{ - id: id, - roomType: roomType, - playType: playType, - gameNo: "", - timeTypes: make(map[TimerType]uint32), - subRoom: subRoom, - sTimer: sTimer, - sender: sender, + id: id, + roomType: roomType, + playType: playType, + gameNo: "", + timeTypes: make(map[TimerType]uint32), + srv: srv, + processor: processor.NewProcessor(), + timeProcessor: processor.NewProcessor(), } return room, pb.ErrCode_OK @@ -44,10 +47,6 @@ func (r *BaseRoom[Seat]) RoomType() int { return r.roomType } -func (r *BaseRoom[Seat]) SetTruthRoom(rm IRoom) { - r.subRoom = rm -} - func (r *BaseRoom[Seat]) PlayType() int { return r.playType } @@ -121,14 +120,6 @@ func (r *BaseRoom[Seat]) RemovePlayer(player IPlayer) { } } -func (r *BaseRoom[Seat]) OnMessage(cmd int32, params ...any) { - if r.subRoom == nil { - log.Error(r.Log("sub room is nil")) - return - } - r.subRoom.OnMessage(cmd, params...) -} - func (r *BaseRoom[Seat]) DebugSendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) { log.Debug(r.UserLog(user.Id(), "send msg:%v %v", msgId, msg.String())) } @@ -140,7 +131,8 @@ func (r *BaseRoom[Seat]) SendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message } else { for _, seat := range r.Seats { if !seat.Empty() && seat.Player().Id() == user.Id() { - r.sender.SendMsg(user, msgId, msg) + iMsg := ipb.MakeMsgEx(r.srv.Name(), 0, user.Id(), int32(msgId), msg) + _ = r.srv.Send(user.GateTopicName(), iMsg) break } } @@ -165,11 +157,7 @@ func (r *BaseRoom[Seat]) Broadcast(msgId pb.MsgId, msg proto.Message, exclude .. } } -func (r *BaseRoom[Seat]) NewTimer(timerType TimerType, duration time.Duration, args ...interface{}) { - if r.sTimer == nil { - log.Error(r.Log("timer handler is nil")) - return - } +func (r *BaseRoom[Seat]) NewTimer(timerType TimerType, duration time.Duration, args ...any) { if _, ok := r.timeTypes[timerType]; ok { log.Error(r.Log("timer type:%v is exist.can not new timer", timerType.String())) // if timerType == TtPlayerAct { @@ -177,8 +165,10 @@ func (r *BaseRoom[Seat]) NewTimer(timerType TimerType, duration time.Duration, a // } return } - tid := r.sTimer.NewTimer(duration+time.Duration(10)*time.Millisecond, func() { - r.sTimer.OnTimer(timerType, args...) + tid := r.srv.NewTimer(duration+time.Duration(10)*time.Millisecond, func() { + if err := r.timeProcessor.Dispatch(int32(timerType), args...); err != nil { + log.ErrorF(r.Log("timer dispatch err:%v", err)) + } }, true, r.Log("start type:%v timer", timerType.String())) r.timeTypes[timerType] = tid // log.Debug(r.Log("start type:%v timer", timerType.String())) @@ -186,7 +176,7 @@ func (r *BaseRoom[Seat]) NewTimer(timerType TimerType, duration time.Duration, a func (r *BaseRoom[Seat]) CancelTimer(timerType TimerType) { if tid, ok := r.timeTypes[timerType]; ok { - r.sTimer.CancelTimer(tid) + r.srv.CancelTimer(tid) delete(r.timeTypes, timerType) // log.Debug(r.Log("stop type:%v timer, rest timer:%+v", timerType.String(), r.timeTypes)) } @@ -194,7 +184,7 @@ func (r *BaseRoom[Seat]) CancelTimer(timerType TimerType) { func (r *BaseRoom[Seat]) CancelAllTimer() { for _, tid := range r.timeTypes { - r.sTimer.CancelTimer(tid) + r.srv.CancelTimer(tid) // log.Debug(r.Log("start type:%v timer", timerType.String())) } r.timeTypes = make(map[TimerType]uint32) @@ -233,12 +223,34 @@ func (r *BaseRoom[Seat]) UserLog(uid int64, format string, a ...any) string { } } -// 玩家俱乐部房间玩游戏 -func (r *BaseRoom[Seat]) GameStart() { - +func (r *BaseRoom[Seat]) Unmarshal(cmd int32, data []byte) (any, error) { + return r.processor.Unmarshal(cmd, data) } -// 玩家俱乐部房间玩游戏 -func (r *BaseRoom[Seat]) GameEnd() { +func (r *BaseRoom[Seat]) Dispatch(user IPlayer, cmd int32, params ...any) error { + inp := make([]any, len(params)+1) + inp = append(inp, user) + inp = append(inp, params...) + return r.processor.Dispatch(cmd, inp...) +} + +func (r *BaseRoom[Seat]) RegisterMessages(metas processor.RegisterMetas) { + r.processor.RegisterMessages(metas) +} + +func (r *BaseRoom[Seat]) timerDispatch(user IPlayer, cmd int32, params ...any) error { + inp := make([]any, len(params)+1) + inp = append(inp, user) + inp = append(inp, params...) + return r.timeProcessor.Dispatch(cmd, inp...) +} + +// 注册时间事件及处理 +func (r *BaseRoom[Seat]) RegisterTimerMessages(metas processor.RegisterMetas) { + r.timeProcessor.RegisterMessages(metas) +} + +// 初始化房间 +func (r *BaseRoom[Seat]) OnInit() { } diff --git a/common/baseroom/interface.go b/common/baseroom/interface.go index a7c2216..47d9754 100644 --- a/common/baseroom/interface.go +++ b/common/baseroom/interface.go @@ -2,18 +2,18 @@ package baseroom import ( "game/common/proto/pb" - "github.com/golang/protobuf/proto" - "time" ) type IRoom interface { Id() int RoomType() int // 房间配置id PlayType() int + OnInit() // SeatPlayerNum() int // HasEmptySeat() bool // HasPlayer(uid int64) bool - OnMessage(cmd int32, params ...any) + Unmarshal(cmd int32, data []byte) (any, error) + Dispatch(user IPlayer, cmd int32, params ...any) error // ReleaseRoom() } @@ -29,22 +29,14 @@ type ISeat interface { type IPlayer interface { Id() int64 Robot() IRobot + GateTopicName() string + RoomId() int } type IRobot interface { OnMessage(cmd pb.MsgId, params ...any) } -type ITimer interface { - OnTimer(timerType TimerType, args ...interface{}) - NewTimer(duration time.Duration, cb func(), needLog bool, desc ...string) uint32 - CancelTimer(timerId uint32) -} - -type ISender interface { - SendMsg(user IPlayer, msgId pb.MsgId, msg proto.Message) -} - type ICreateRoom interface { CreateRoom(id, roomType int) (IRoom, pb.ErrCode) } diff --git a/common/userBindService/userService.go b/common/userBindService/userService.go index 0f1344b..b0c1950 100644 --- a/common/userBindService/userService.go +++ b/common/userBindService/userService.go @@ -8,6 +8,7 @@ import ( "game/common/utils" "github.com/fox/fox/etcd" "github.com/fox/fox/log" + "github.com/fox/fox/service" "github.com/fox/fox/xrand" "github.com/go-redis/redis/v8" "time" @@ -113,7 +114,7 @@ func (m *UserBindService) RandServiceNode(typeId pb.ServiceTypeId) (*etcd.Servic } // 根据服务类型,路由到对应的服务节点 -func (m *UserBindService) FindServiceName(userId int64, typeId pb.ServiceTypeId) (string, error) { +func (m *UserBindService) findServiceName(userId int64, typeId pb.ServiceTypeId) (string, error) { if userId > 0 { // 向redis中查询。redis中保留的服务节点不一定是可用的,还需要向etcd中验证 sName := m.LoadFromRedis(userId, typeId) @@ -134,3 +135,16 @@ func (m *UserBindService) FindServiceName(userId int64, typeId pb.ServiceTypeId) m.rdb.Set(context.Background(), m.makeRedisKey(userId, typeId), node.Name, 2*24*time.Hour) return node.Name, nil } + +/* +查找topic,根据serviceTypeId以及玩家id查找玩家过往访问该服务的节点,优先使用原节点 +*/ +func (m *UserBindService) FindTopic(userId int64, serviceTypeId pb.ServiceTypeId) (topic, sName string) { + var err error + if sName, err = m.findServiceName(userId, serviceTypeId); err == nil { + return service.TopicEx(sName), sName + } else { + log.Error(err.Error()) + } + return "", sName +} diff --git a/server/chat/server/processor.go b/server/chat/server/processor.go index f0b39a9..ba59b48 100644 --- a/server/chat/server/processor.go +++ b/server/chat/server/processor.go @@ -18,12 +18,11 @@ func (s *ChatService) initProcessor() { func (s *ChatService) onChat(uid int64, msg *pb.C2SChat) { switch msg.Type { case pb.ChatType_CT_Private: - sName, err := s.bindService.FindServiceName(msg.DstUser.UserId, pb.ServiceTypeId_STI_Gate) - if err != nil { - log.DebugF("find user:%v in gate err: %v", uid, err) - return + tName, _ := s.bindService.FindTopic(msg.DstUser.UserId, pb.ServiceTypeId_STI_Gate) + if tName == "" { + log.DebugF("user:%v find gate failed", uid) } - s.SendServiceMsg(service.TopicEx(sName), msg.DstUser.UserId, int32(pb.MsgId_S2CChatId), msg) + s.SendServiceMsg(service.TopicEx(tName), msg.DstUser.UserId, int32(pb.MsgId_S2CChatId), msg) default: s.SendServiceMsg(service.TopicEx(topicName.WorldMessage), uid, int32(pb.MsgId_S2CChatId), msg) } diff --git a/server/colorgame/room/colorRoom.go b/server/colorgame/room/colorRoom.go index 5359605..b0526bb 100644 --- a/server/colorgame/room/colorRoom.go +++ b/server/colorgame/room/colorRoom.go @@ -4,19 +4,26 @@ import ( "game/common/baseroom" "game/common/proto/pb" "github.com/fox/fox/log" + "github.com/fox/fox/service" ) type ColorRoom struct { *baseroom.BaseRoom[ColorSeat] } -func newColorRoom(id, roomType int) (baseroom.IRoom, pb.ErrCode) { +func newColorRoom(id, roomType int, srv service.IService) (baseroom.IRoom, pb.ErrCode) { rm := &ColorRoom{} playType := 0 code := pb.ErrCode_OK - rm.BaseRoom, code = baseroom.NewBaseRoom[ColorSeat](id, roomType, playType, rm, rm, rm) + rm.BaseRoom, code = baseroom.NewBaseRoom[ColorSeat](id, roomType, playType, srv) if code != pb.ErrCode_OK { log.ErrorF("new color room err code:%v", code) + return nil, code } + rm.OnInit() return rm, code } + +func (rm *ColorRoom) OnInit() { + return +} diff --git a/server/colorgame/server/processor.go b/server/colorgame/server/processor.go index f4f7f27..1b68f17 100644 --- a/server/colorgame/server/processor.go +++ b/server/colorgame/server/processor.go @@ -1,156 +1,15 @@ package server import ( - "encoding/json" - "game/common/model/user" - "game/common/proto/pb" - "game/common/rpcName" - "game/common/utils" - "github.com/fox/fox/etcd" - "github.com/fox/fox/ipb" - "github.com/fox/fox/ksync" - "github.com/fox/fox/log" "github.com/fox/fox/processor" - "github.com/fox/fox/service" - "time" ) const ( - timeout = time.Second * 30 +// timeout = time.Second * 30 ) func (s *ColorService) initProcessor() { s.processor.RegisterMessages(processor.RegisterMetas{ - pb.MsgId_C2SUserLoginId: {pb.C2SUserLogin{}, s.onLoginOrRegister}, + //pb.MsgId_C2SUserLoginId: {pb.C2SUserLogin{}, s.onLoginOrRegister}, }) } - -func (s *ColorService) checkLoginOrRegister(req *pb.C2SUserLogin) (us *user.UserAccount, code pb.ErrCode, node *etcd.ServiceNode) { - var err error - node, err = s.bindService.RandServiceNode(pb.ServiceTypeId_STI_DB) - if err != nil { - log.ErrorF(s.Log("not find db service.err:%s ", err.Error())) - return nil, pb.ErrCode_SystemErr, node - } - if req.Version < "20250601123030" { - return nil, pb.ErrCode_VersionTooLow, node - } - us = &user.UserAccount{ - Username: req.Username, - Password: req.Password, - DeviceID: req.DeviceId, - LastLoginIP: req.Ip, - } - rpcMsg := ipb.MakeRpcMsg[user.UserAccount](rpcName.GetUserAccount, 0, 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, node - } - _ = json.Unmarshal(rspMsg.Msg, us) - //log.DebugF("收到rpc:%v返回数据:%v", rpcName.GetUserAccount, string(rspMsg.Msg)) - if us.ID == 0 { - // 没有帐号,创建帐号 - us = &user.UserAccount{ - Username: req.Username, - Password: req.Password, - DeviceID: req.DeviceId, - LastLoginIP: req.Ip, - LastLoginTime: time.Now(), - RegisterIP: req.Ip, - RegisterTime: time.Now(), - } - rpcMsg = ipb.MakeRpcMsg[user.UserAccount](rpcName.CreateUserAccount, 0, 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, node - } - _ = json.Unmarshal(rspMsg.Msg, us) - if us.ID == 0 { - log.ErrorF(s.Log("call rpc:%v err", rpcMsg.RpcMsgId)) - return nil, pb.ErrCode_SystemErr, node - } - log.DebugF("收到rcp:%v返回数据:%v", rpcName.CreateUserAccount, string(rspMsg.Msg)) - } - if !utils.CheckPassword(req.Password, us.Password) { - log.ErrorF(s.Log("用户密码:%v 数据库中密码:%v", req.Password, us.Password)) - return nil, pb.ErrCode_LoginUserOrPwdErr, node - } - switch us.Status { - case user.AccountFrozen: - return nil, pb.ErrCode_AccountFrozen, node - case user.AccountBanned: - return nil, pb.ErrCode_AccountBanned, node - default: - return us, pb.ErrCode_OK, node - } -} - -// 生成JWT令牌(简化版) -func generateToken(userID int64, username string) (string, error) { - _ = userID - _ = username - // 这里应该使用JWT库生成实际令牌 - // 简化实现,实际项目中请使用安全的JWT实现 - return "generated-token-placeholder", nil -} - -// 获取用户数据,如果没有则创建 -func (s *ColorService) getUser(accountId int64, tName string) (*user.User, pb.ErrCode) { - us := &user.User{ - AccountId: accountId, - } - rpcMsg := ipb.MakeRpcMsg[user.User](rpcName.GetUserByAccountId, 0, us) - rsp, err := s.Call(service.RpcTopicEx(tName), 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(rsp.Msg, us) - if us.ID == 0 { - log.ErrorF(s.Log("call rpc:%v return:%v", rpcMsg.RpcMsgId, string(rsp.Msg))) - return us, pb.ErrCode_SystemErr - } - return us, pb.ErrCode_OK -} - -// 登录或注册 -func (s *ColorService) onLoginOrRegister(iMsg *ipb.InternalMsg, req *pb.C2SUserLogin) { - ksync.GoSafe(func() { - account, code, node := s.checkLoginOrRegister(req) - userId := int64(0) - rsp := &pb.S2CUserLogin{Code: code} - if account != nil && code == pb.ErrCode_OK { - // 拉取用户数据 - us := &user.User{} - us, code = s.getUser(userId, req.Username) - if code == pb.ErrCode_OK { - rsp.UserId = us.ID - rsp.Token, _ = generateToken(account.ID, account.Username) - userId = rsp.UserId - } - } - s.SendServiceMsg(service.TopicEx(iMsg.ServiceName), iMsg.ConnId, userId, int32(pb.MsgId_S2CUserLoginId), rsp) - - if account != nil && account.ID > 0 { - loginLog := &user.UserLoginLog{ - AccountID: account.ID, - LoginIP: req.Ip, - LoginTime: time.Now(), - DeviceInfo: req.DeviceId, - LoginResult: code == pb.ErrCode_OK, - FailReason: code.String(), - } - switch code { - case pb.ErrCode_LoginUserOrPwdErr, pb.ErrCode_OK: - rpcMsg := ipb.MakeRpcMsg[user.UserLoginLog](rpcName.LogUserAccountLogin, 0, loginLog) - ksync.GoSafe(func() { - _, _ = s.Call(service.RpcTopicEx(node.Name), timeout, rpcMsg) - }, nil) - - } - } - - }, nil) -} diff --git a/server/colorgame/server/service.go b/server/colorgame/server/service.go index 5893663..01894f6 100644 --- a/server/colorgame/server/service.go +++ b/server/colorgame/server/service.go @@ -89,6 +89,16 @@ func (s *ColorService) NotifyStop() { } func (s *ColorService) CanStop() bool { + switch s.roomMgr.Status() { + case baseroom.SsWorking: + return false + case baseroom.SsWaitStop, baseroom.SsStopped: + // 没有玩家 + if s.playerMgr.Count() == 0 { + return true + } + return false + } return true } @@ -97,6 +107,18 @@ func (s *ColorService) OnStop() { log.Debug("OnStop") } +func (s *ColorService) findRoom(uid int64) (baseroom.IPlayer, baseroom.IRoom, error) { + if user := s.playerMgr.Find(uid); user != nil { + if rm := s.roomMgr.Find(user.RoomId()); rm != nil { + return user, rm, nil + } else { + return nil, nil, fmt.Errorf("user:%v not found room %d", uid, user.RoomId()) + } + } else { + return nil, nil, fmt.Errorf("user:%v not exist", uid) + } +} + // 处理其它服发送过来的消息 func (s *ColorService) OnMessage(data []byte) error { var iMsg = &ipb.InternalMsg{} @@ -108,7 +130,16 @@ func (s *ColorService) OnMessage(data []byte) error { 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()) + var user baseroom.IPlayer + var rm baseroom.IRoom + if user, rm, err = s.findRoom(iMsg.UserId); err == nil { + if req, err = rm.Unmarshal(iMsg.MsgId, iMsg.Msg); err == nil { + err = rm.Dispatch(user, iMsg.MsgId, iMsg, req) + } + } + if err != nil { + log.Error(err.Error()) + } } log.Debug(s.Log("received message:%v", iMsg.MsgId)) return nil diff --git a/server/gate/server/service.go b/server/gate/server/service.go index 7998c96..9eee3a5 100644 --- a/server/gate/server/service.go +++ b/server/gate/server/service.go @@ -152,19 +152,6 @@ func (s *GateService) OnMessage(data []byte) error { return nil } -/* -查找topic,根据serviceTypeId以及玩家id查找玩家过往访问该服务的节点,优先使用原节点 -*/ -func (s *GateService) findTopic(userId int64, serviceTypeId pb.ServiceTypeId) (topic, sName string) { - var err error - if sName, err = s.bindService.FindServiceName(userId, serviceTypeId); err == nil { - return service.TopicEx(sName), sName - } else { - log.Error(err.Error()) - } - return "", sName -} - // 运行在conn的read协程里。将消息转发给内部服务 func (s *GateService) WsOnMessage(conn ws.IConn, data []byte) { msg := &pb.ClientMsg{} @@ -179,7 +166,7 @@ func (s *GateService) WsOnMessage(conn ws.IConn, data []byte) { if msg.ServiceName != "" { topic = service.TopicEx(msg.ServiceName) } else { - topic, msg.ServiceName = s.findTopic(conn.UserId(), msg.ServiceTid) + topic, msg.ServiceName = s.bindService.FindTopic(conn.UserId(), msg.ServiceTid) } if topic != "" { if msg.MsgId == int32(pb.MsgId_C2SUserLoginId) {