diff --git a/common/rpc/rpcGameUser.go b/common/rpc/rpcGameUser.go index 359a86e..b423911 100644 --- a/common/rpc/rpcGameUser.go +++ b/common/rpc/rpcGameUser.go @@ -25,6 +25,7 @@ func RpcGetGameUser(s service.IService, uid int64) (*user.GameUser, pb.ErrCode) }, } rpcMsg := ipb.MakeRpcMsg(GetGameUser, uid, us) + log.DebugF("%v call rpc:%v", s.Name(), GetGameUser) rspMsg, err := s.CallByServiceId(int(pb.ServiceTypeId_STI_DB), timeout, rpcMsg) if err != nil { log.ErrorF("call rpc:%v err:%s ", rpcMsg.RpcMsgId, err.Error()) diff --git a/common/userBindService/userService.go b/common/userBindService/userService.go index 6eb4cc1..4c401ad 100644 --- a/common/userBindService/userService.go +++ b/common/userBindService/userService.go @@ -73,6 +73,10 @@ func (m *UserBindService) DelUserService(userId int64, typeId pb.ServiceTypeId) // 保存玩家与节点的绑定关系至从redis func (m *UserBindService) SaveUserService(userId int64, typeId pb.ServiceTypeId, serviceName string) { + if userId == 0 { + log.Error("玩家不能为0") + return + } key := m.makeRedisKey(userId) subKey := m.makeRedisSubKey(typeId) m.rdb.HSet(context.Background(), key, subKey, serviceName) diff --git a/server/client/server/login.go b/server/client/server/login.go index ae48a92..c847092 100644 --- a/server/client/server/login.go +++ b/server/client/server/login.go @@ -29,6 +29,16 @@ func (s *ClientService) onLogin(cMsg *pb.ClientMsg, msg *pb.RspUserLogin) { s.reqMatchRoom() } +// 收到登陆成功消息,判断是否顶号 +func (s *ClientService) onLogout(cMsg *pb.ClientMsg, msg *pb.RspUserLogout) { + if msg.Code != pb.ErrCode_OK { + log.ErrorF("onLogout error:%v :%v", msg.Code, msg.Code.String()) + return + } + _ = cMsg + log.DebugF("user:%v logout", s.userId) +} + func (s *ClientService) reqMatchRoom() { log.DebugF(s.Log("ready reqMatch")) s.SendMsg(pb.ServiceTypeId_STI_Match, int32(pb.MsgId_ReqMatchRoomId), &pb.ReqMatchRoom{ diff --git a/server/client/server/processor.go b/server/client/server/processor.go index 8fc449d..65d6795 100644 --- a/server/client/server/processor.go +++ b/server/client/server/processor.go @@ -7,8 +7,9 @@ import ( func (s *ClientService) initProcessor() { s.processor.RegisterMessages(processor.RegisterMetas{ - pb.MsgId_RspUserLoginId: {pb.RspUserLogin{}, s.onLogin}, - pb.MsgId_RspMatchRoomId: {pb.RspMatchRoom{}, s.onMatchRoom}, - pb.MsgId_RspEnterRoomId: {pb.RspEnterRoom{}, s.onEnterRoom}, + pb.MsgId_RspUserLoginId: {pb.RspUserLogin{}, s.onLogin}, + pb.MsgId_RspMatchRoomId: {pb.RspMatchRoom{}, s.onMatchRoom}, + pb.MsgId_RspEnterRoomId: {pb.RspEnterRoom{}, s.onEnterRoom}, + pb.MsgId_RspUserLogoutId: {pb.RspUserLogout{}, s.onLogout}, }) } diff --git a/server/client/server/service.go b/server/client/server/service.go index 63c4bcf..fe7d3ae 100644 --- a/server/client/server/service.go +++ b/server/client/server/service.go @@ -7,6 +7,7 @@ import ( "github.com/fox/fox/log" "github.com/fox/fox/processor" "github.com/fox/fox/ws" + "github.com/fox/fox/xrand" "github.com/golang/protobuf/proto" "time" ) @@ -47,7 +48,7 @@ func newClientService(serviceId int) *ClientService { s.username = fmt.Sprintf("test%04d", serviceId) s.password = "123456" size := len(config.ClientCfg.Address) - addr := config.ClientCfg.Address[serviceId%size] + addr := config.ClientCfg.Address[xrand.RandN(size)] wsAddr := fmt.Sprintf("ws://%v", addr) if s.client, err = ws.NewClient(wsAddr, s); err != nil { log.FatalF("connect url:%v err:%v", wsAddr, err.Error()) diff --git a/server/gate/server/processor.go b/server/gate/server/processor.go index b5249e8..552d74b 100644 --- a/server/gate/server/processor.go +++ b/server/gate/server/processor.go @@ -4,9 +4,11 @@ import ( "game/common/proto/pb" "game/common/topicName" "github.com/fox/fox/ipb" + "github.com/fox/fox/log" "github.com/fox/fox/processor" - "github.com/fox/fox/service" "github.com/fox/fox/ws" + "reflect" + "time" ) /* @@ -16,12 +18,13 @@ func (s *GateService) initProcessor() { s.Processor().RegisterMessages(processor.RegisterMetas{ pb.MsgId_RspUserLoginId: {pb.RspUserLogin{}, s.onUserLogin}, pb.MsgId_RspUserLogoutId: {pb.RspUserLogout{}, s.onUserLogout}, + pb.MsgId_NtfUserOnlineId: {pb.NtfUserOnline{}, s.onUserOnline}, }) } // 收到登陆成功消息,判断是否顶号 func (s *GateService) onUserLogin(iMsg *ipb.InternalMsg, conn ws.IConn, msg *pb.RspUserLogin) { - if conn == nil { + if conn == nil || reflect.ValueOf(conn).IsNil() { return } // 登陆失败,回传玩家 @@ -29,24 +32,61 @@ func (s *GateService) onUserLogin(iMsg *ipb.InternalMsg, conn ws.IConn, msg *pb. s.SendClientMsg(conn, iMsg.ServiceName, int32(pb.MsgId_RspUserLoginId), msg) return } - s.SendClientMsg(conn, iMsg.ServiceName, int32(pb.MsgId_RspUserLoginId), msg) - s.wss.SetUserId(conn.Id(), msg.UserId) - sName := s.BindService().LoadFromRedis(conn.UserId(), pb.ServiceTypeId_STI_Gate) - // 网关不同,说明玩家在其它网关上登陆, - if sName != "" && sName != s.Name() { - s.SendServiceMsg(service.TopicEx(sName), conn, int32(pb.MsgId_RspUserLogoutId), &pb.RspUserLogout{Code: pb.ErrCode_LoginDiffLoc}) + log.Debug(s.Log("玩家id:%v登陆成功,连接:%v ", msg.UserId, conn.Id())) + if oldConn, ok := s.wss.FindConnByUserId(msg.UserId); ok { + log.Debug(s.Log("通知本网关下的旧玩家id:%v下线,连接:%v 当前连接:%v", oldConn.UserId(), oldConn.Id(), conn.Id())) + s.SendClientMsg(oldConn, iMsg.ServiceName, int32(pb.MsgId_RspUserLogoutId), &pb.RspUserLogout{Code: pb.ErrCode_LoginDiffLoc}) + s.wss.SetUserId(oldConn.Id(), 0) + // 登出的清理工作由WsOnDisconnect实现 + s.Timer.NewTimer(time.Second, func() { + oldConn.Close() + }, false) } + //log.Debug(s.Log("连接:%v 设置玩家id:%v", conn.Id(), msg.UserId)) + s.wss.SetUserId(conn.Id(), msg.UserId) + s.SendClientMsg(conn, iMsg.ServiceName, int32(pb.MsgId_RspUserLoginId), msg) + //sName := s.BindService().LoadFromRedis(conn.UserId(), pb.ServiceTypeId_STI_Gate) + //// 网关不同,说明玩家在其它网关上登陆, + //if sName != "" && sName != s.Name() { + // log.Debug(s.Log("通知其它网关:%v下的旧玩家id:%v下线", sName, conn.UserId())) + // s.SendServiceMsg(service.TopicEx(sName), conn, int32(pb.MsgId_RspUserLogoutId), &pb.RspUserLogout{Code: pb.ErrCode_LoginDiffLoc}) + //} + //log.Debug(s.Log("玩家:%v 绑定网关:%v", conn.UserId(), s.Name())) s.BindService().SaveUserService(msg.UserId, pb.ServiceTypeId_STI_Gate, s.Name()) // 广播玩家上线 s.SendServiceMsg(topicName.UserOnline, conn, int32(pb.MsgId_NtfUserOnlineId), &pb.NtfUserOnline{UserId: msg.UserId}) } -// 收到登出消息 +// 收到登出消息,网关不会主动发登出消息,异地登陆由online广播中检查自己 func (s *GateService) onUserLogout(iMsg *ipb.InternalMsg, conn ws.IConn, msg *pb.RspUserLogout) { - if conn == nil { + if conn == nil || reflect.ValueOf(conn).IsNil() { return } s.SendClientMsg(conn, iMsg.ServiceName, int32(pb.MsgId_RspUserLogoutId), msg) + // 登出的清理工作由WsOnDisconnect实现 - conn.Close() + s.Timer.NewTimer(time.Second, func() { + conn.Close() + }, false) +} + +// 收到玩家上线广播 +func (s *GateService) onUserOnline(iMsg *ipb.InternalMsg, conn ws.IConn, msg *pb.NtfUserOnline) { + //log.DebugF(s.Log("收到玩家:%v上线广播消息", msg.UserId)) + if conn == nil || reflect.ValueOf(conn).IsNil() { + log.Error(s.Log("连接不存在,玩家:%v", msg.UserId)) + return + } + sName := s.BindService().LoadFromRedis(conn.UserId(), pb.ServiceTypeId_STI_Gate) + //log.Debug(s.Log("玩家:%v 当前网关:%v", msg.UserId, sName)) + // 网关不同,说明玩家在其它网关上登陆, + if sName != "" && sName != s.Name() { + log.Debug(s.Log("通知本网关:%v下的旧玩家id:%v下线", s.Name(), conn.UserId())) + s.SendClientMsg(conn, iMsg.ServiceName, int32(pb.MsgId_RspUserLogoutId), &pb.RspUserLogout{Code: pb.ErrCode_LoginDiffLoc}) + s.wss.SetUserId(conn.Id(), 0) + // 登出的清理工作由WsOnDisconnect实现 + s.Timer.NewTimer(time.Second, func() { + conn.Close() + }, false) + } } diff --git a/server/gate/server/service.go b/server/gate/server/service.go index 74c959f..16c7cdc 100644 --- a/server/gate/server/service.go +++ b/server/gate/server/service.go @@ -13,6 +13,7 @@ import ( "github.com/fox/fox/ws" "github.com/golang/protobuf/proto" "github.com/nats-io/nats.go" + "reflect" ) var Gates []*GateService @@ -55,6 +56,7 @@ func newGateService(serviceId int) *GateService { TypeId: int(pb.ServiceTypeId_STI_Gate), Version: config.Cfg.BuildDate, }, &config.Cfg.Redis) + _ = s.GameService.Subscribe(topicName.UserOnline) addressPos := serviceId - config.Command.ServiceId if len(config.GateCfg.Address) <= addressPos { @@ -98,6 +100,20 @@ func (s *GateService) CanStop() bool { } func (s *GateService) OnStop() { + s.wss.Rang(func(conn ws.IConn) bool { + if conn.UserId() > 0 { + sName := s.BindService().LoadFromRedis(conn.UserId(), pb.ServiceTypeId_STI_Gate) + // 相同网关,则为正常下线,删除redis信息然后向内网广播下线 + // 不同网关,异地登陆顶号 由其它网关通知本网关向玩家发送顶号消息(processor中处理),并主动关闭连接,不广播下线消息 + if sName == s.Name() { + log.Debug(s.Log("玩家:%v 解绑网关:%v", conn.UserId(), s.Name())) + s.BindService().DelUserService(conn.UserId(), pb.ServiceTypeId_STI_Gate) + s.SendServiceMsg(topicName.UserOffline, conn, int32(pb.MsgId_NtfUserOfflineId), &pb.NtfUserOffline{UserId: conn.UserId()}) + } + } + return true + + }) s.NatsService.OnStop() log.Debug("OnStop") } @@ -120,7 +136,7 @@ func (s *GateService) connMessage(conn ws.IConn, iMsg *ipb.InternalMsg) { } else { s.SendClientData(conn, iMsg.ServiceName, iMsg.MsgId, iMsg.Msg) } - //log.Debug(s.Log("received service message:%v", iMsg.MsgId)) + //log.Debug(s.Log("received service message:%v", pb.MsgId(iMsg.MsgId))) } // 处理其它服发送过来的消息。大部分是将消息转发给玩家 @@ -133,8 +149,8 @@ func (s *GateService) OnMessage(data []byte) error { } // conn不能为空,全服广播消息走WorldMessage var conn = s.findConn(iMsg) - if conn == nil { - log.WarnF(s.Log("client not exist.msg:%v", iMsg.MsgId)) + if conn == nil || reflect.ValueOf(conn).IsNil() { + //log.Error(s.Log("玩家:%v 连接:%v 不存在.msg:%v", iMsg.UserId, iMsg.ConnId, iMsg.MsgId)) return nil } s.connMessage(conn, iMsg) @@ -168,6 +184,11 @@ func (s *GateService) WsOnMessage(conn ws.IConn, data []byte) { _ = proto.Unmarshal(msg.Data, req) req.Ip = conn.Addr() msg.Data, _ = proto.Marshal(req) + } else { + if conn.UserId() == 0 { + log.Error(s.Log("玩家uid不能为0,连接:%v", conn.Id())) + return + } } s.SendServiceData(topic, conn, msg.MsgId, msg.Data) } else { @@ -184,7 +205,7 @@ func (s *GateService) SendServiceData(topic string, conn ws.IConn, msgId int32, // 向内部服务发送消息 func (s *GateService) SendServiceMsg(topic string, conn ws.IConn, msgId int32, msg proto.Message) { - log.DebugF("user:%v send to service:%v msg id:%v, msg:%v", conn.UserId(), topic, pb.MsgId(msgId), msg.String()) + log.Debug(s.Log("user:%v send to service:%v msg id:%v, msg:%v", conn.UserId(), topic, pb.MsgId(msgId), msg.String())) data, _ := proto.Marshal(msg) s.SendServiceData(topic, conn, msgId, data) } @@ -198,7 +219,7 @@ func (s *GateService) SendClientData(conn ws.IConn, serviceName string, msgId in // 向玩家发送消息 func (s *GateService) SendClientMsg(conn ws.IConn, serviceName string, msgId int32, msg proto.Message) { - log.DebugF("send to user:%v msg id:%v, msg:%v", conn.UserId(), pb.MsgId(msgId), msg.String()) + log.Debug(s.Log("send to user:%v msg id:%v, msg:%v", conn.UserId(), pb.MsgId(msgId), msg.String())) data, _ := proto.Marshal(msg) s.SendClientData(conn, serviceName, msgId, data) } @@ -209,6 +230,7 @@ func (s *GateService) WsOnDisconnect(conn ws.IConn) { // 相同网关,则为正常下线,删除redis信息然后向内网广播下线 // 不同网关,异地登陆顶号 由其它网关通知本网关向玩家发送顶号消息(processor中处理),并主动关闭连接,不广播下线消息 if sName == s.Name() { + log.Debug(s.Log("玩家:%v 解绑网关:%v", conn.UserId(), s.Name())) s.BindService().DelUserService(conn.UserId(), pb.ServiceTypeId_STI_Gate) s.SendServiceMsg(topicName.UserOffline, conn, int32(pb.MsgId_NtfUserOfflineId), &pb.NtfUserOffline{UserId: conn.UserId()}) }