diff --git a/common/mq/nats.go b/common/mq/nats.go index 5b2cf5b7..f37c5abb 100644 --- a/common/mq/nats.go +++ b/common/mq/nats.go @@ -40,6 +40,11 @@ func NewMessageQueue(serverType string, appId string) (r *MessageQueue) { logger.Error("nats subscribe error: %v", err) return nil } + _, err = r.natsConn.ChanSubscribe("ALL_SERVER_HK4E", r.natsMsgChan) + if err != nil { + logger.Error("nats subscribe error: %v", err) + return nil + } r.netMsgInput = make(chan *NetMsg, 1000) r.netMsgOutput = make(chan *NetMsg, 1000) r.cmdProtoMap = cmd.NewCmdProtoMap() @@ -85,8 +90,6 @@ func (m *MessageQueue) recvHandler() { } gameMsg.PayloadMessage = payloadMessage } - case MsgTypeFight: - case MsgTypeConnCtrl: } m.netMsgOutput <- netMsg } @@ -107,8 +110,6 @@ func (m *MessageQueue) sendHandler() { } gameMsg.PayloadMessageData = payloadMessageData } - case MsgTypeFight: - case MsgTypeConnCtrl: } // msgpack NetMsg netMsgData, err := msgpack.Marshal(netMsg) diff --git a/common/mq/net_msg.go b/common/mq/net_msg.go index cda505da..4afd810f 100644 --- a/common/mq/net_msg.go +++ b/common/mq/net_msg.go @@ -6,6 +6,7 @@ const ( MsgTypeGame = iota MsgTypeFight MsgTypeConnCtrl + MsgTypeServer ) type NetMsg struct { @@ -15,6 +16,7 @@ type NetMsg struct { GameMsg *GameMsg `msgpack:"GameMsg"` FightMsg *FightMsg `msgpack:"FightMsg"` ConnCtrlMsg *ConnCtrlMsg `msgpack:"ConnCtrlMsg"` + ServerMsg *ServerMsg `msgpack:"ServerMsg"` OriginServerType string `msgpack:"OriginServerType"` OriginServerAppId string `msgpack:"OriginServerAppId"` } @@ -35,17 +37,15 @@ type GameMsg struct { const ( ClientRttNotify = iota ClientTimeNotify - FightServerSelectNotify KickPlayerNotify ) type ConnCtrlMsg struct { - UserId uint32 `msgpack:"UserId"` - ClientRtt uint32 `msgpack:"ClientRtt"` - ClientTime uint32 `msgpack:"ClientTime"` - FightServerAppId string `msgpack:"FightServerAppId"` - KickUserId uint32 `msgpack:"KickUserId"` - KickReason uint32 `msgpack:"KickReason"` + UserId uint32 `msgpack:"UserId"` + ClientRtt uint32 `msgpack:"ClientRtt"` + ClientTime uint32 `msgpack:"ClientTime"` + KickUserId uint32 `msgpack:"KickUserId"` + KickReason uint32 `msgpack:"KickReason"` } const ( @@ -63,3 +63,37 @@ type FightMsg struct { AvatarGuid uint64 `msgpack:"AvatarGuid"` GateServerAppId string `msgpack:"GateServerAppId"` } + +const ( + ServerAppidBindNotify = iota + ServerUserOnlineStateChangeNotify + ServerGetUserBaseInfoReq + ServerGetUserBaseInfoRsp + ServerUserGsChangeNotify +) + +type ServerMsg struct { + FightServerAppId string `msgpack:"FightServerAppId"` + UserId uint32 `msgpack:"UserId"` + IsOnline bool `msgpack:"IsOnline"` + UserBaseInfo *UserBaseInfo `msgpack:"UserBaseInfo"` + GameServerAppId string `msgpack:"GameServerAppId"` + JoinHostUserId uint32 `msgpack:"JoinHostUserId"` +} + +type OriginInfo struct { + CmdName string `msgpack:"CmdName"` + UserId uint32 `msgpack:"UserId"` +} + +type UserBaseInfo struct { + OriginInfo *OriginInfo `msgpack:"OriginInfo"` + UserId uint32 `msgpack:"UserId"` + Nickname string `msgpack:"Nickname"` + PlayerLevel uint32 `msgpack:"PlayerLevel"` + MpSettingType uint8 `msgpack:"MpSettingType"` + NameCardId uint32 `msgpack:"NameCardId"` + Signature string `msgpack:"Signature"` + HeadImageId uint32 `msgpack:"HeadImageId"` + WorldPlayerNum uint32 `msgpack:"WorldPlayerNum"` +} diff --git a/common/mq/topic.go b/common/mq/topic.go index d4a92a94..5699eebe 100644 --- a/common/mq/topic.go +++ b/common/mq/topic.go @@ -46,3 +46,11 @@ func (m *MessageQueue) SendToPathfinding(appId string, netMsg *NetMsg) { netMsg.OriginServerAppId = originServerAppId m.netMsgInput <- netMsg } + +func (m *MessageQueue) SendToAll(netMsg *NetMsg) { + netMsg.Topic = "ALL_SERVER_HK4E" + originServerType, originServerAppId := m.getOriginServer() + netMsg.OriginServerType = originServerType + netMsg.OriginServerAppId = originServerAppId + m.netMsgInput <- netMsg +} diff --git a/gate/client_proto/client_proto_gen_test.go b/gate/client_proto/client_proto_gen_test.go index aed077cb..08e628bd 100644 --- a/gate/client_proto/client_proto_gen_test.go +++ b/gate/client_proto/client_proto_gen_test.go @@ -27,7 +27,7 @@ func TestClientProtoGen(t *testing.T) { fileData += "pb \"google.golang.org/protobuf/proto\"\n" fileData += ")\n" fileData += "\n" - fileData += "func (c *ClientCmdProtoMap) GetClientProtoObjByName(protoObjName string) pb.Message {\n" + fileData += "func (c *ClientCmdProtoMap) GetClientProtoObjByName(protoObjName string) any {\n" fileData += "switch protoObjName {\n" for _, protoObjName := range nameList { fileData += "case \"" + protoObjName + "\":\nreturn new(proto." + protoObjName + ")\n" diff --git a/gate/net/forward.go b/gate/net/forward.go index 05ad5f6c..ff99d205 100644 --- a/gate/net/forward.go +++ b/gate/net/forward.go @@ -153,28 +153,33 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session) // 从GS接收消息 func (k *KcpConnectManager) sendMsgHandle() { logger.Debug("send msg handle start") - kcpRawSendChanMap := make(map[uint64]chan *ProtoMsg) + convSessionMap := make(map[uint64]*Session) userIdConvMap := make(map[uint32]uint64) sendToClientFn := func(protoMsg *ProtoMsg) { // 分发到每个连接具体的发送协程 - kcpRawSendChan := kcpRawSendChanMap[protoMsg.ConvId] - if kcpRawSendChan != nil { - select { - case kcpRawSendChan <- protoMsg: - default: - logger.Error("kcpRawSendChan is full, convId: %v", protoMsg.ConvId) - } - } else { - logger.Error("kcpRawSendChan is nil, convId: %v", protoMsg.ConvId) + session := convSessionMap[protoMsg.ConvId] + if session == nil { + logger.Error("session is nil, convId: %v", protoMsg.ConvId) + return + } + kcpRawSendChan := session.kcpRawSendChan + if kcpRawSendChan == nil { + logger.Error("kcpRawSendChan is nil, session: %v", session) + return + } + select { + case kcpRawSendChan <- protoMsg: + default: + logger.Error("kcpRawSendChan is full, session: %v", session) } } for { select { case session := <-k.createSessionChan: - kcpRawSendChanMap[session.conn.GetConv()] = session.kcpRawSendChan + convSessionMap[session.conn.GetConv()] = session userIdConvMap[session.userId] = session.conn.GetConv() case session := <-k.destroySessionChan: - delete(kcpRawSendChanMap, session.conn.GetConv()) + delete(convSessionMap, session.conn.GetConv()) delete(userIdConvMap, session.userId) close(session.kcpRawSendChan) case protoMsg := <-k.localMsgOutput: @@ -182,36 +187,65 @@ func (k *KcpConnectManager) sendMsgHandle() { case netMsg := <-k.messageQueue.GetNetMsg(): switch netMsg.MsgType { case mq.MsgTypeGame: - if netMsg.EventId != mq.NormalMsg { - logger.Error("recv unknown event from game server, event id: %v", netMsg.EventId) - continue - } gameMsg := netMsg.GameMsg - convId, exist := userIdConvMap[gameMsg.UserId] - if !exist { - logger.Error("can not find convId by userId") - continue + switch netMsg.EventId { + case mq.NormalMsg: + convId, exist := userIdConvMap[gameMsg.UserId] + if !exist { + logger.Error("can not find convId by userId") + continue + } + protoMsg := new(ProtoMsg) + protoMsg.ConvId = convId + protoMsg.CmdId = gameMsg.CmdId + protoMsg.HeadMessage = k.getHeadMsg(gameMsg.ClientSeq) + protoMsg.PayloadMessage = gameMsg.PayloadMessage + sendToClientFn(protoMsg) } - protoMsg := new(ProtoMsg) - protoMsg.ConvId = convId - protoMsg.CmdId = gameMsg.CmdId - protoMsg.HeadMessage = k.getHeadMsg(gameMsg.ClientSeq) - protoMsg.PayloadMessage = gameMsg.PayloadMessage - sendToClientFn(protoMsg) case mq.MsgTypeConnCtrl: - if netMsg.EventId != mq.KickPlayerNotify { - continue - } connCtrlMsg := netMsg.ConnCtrlMsg - convId, exist := userIdConvMap[connCtrlMsg.KickUserId] - if !exist { - logger.Error("can not find convId by userId") - continue + switch netMsg.EventId { + case mq.KickPlayerNotify: + convId, exist := userIdConvMap[connCtrlMsg.KickUserId] + if !exist { + logger.Error("can not find convId by userId") + continue + } + k.kcpEventInput <- &KcpEvent{ + ConvId: convId, + EventId: KcpConnForceClose, + EventMessage: connCtrlMsg.KickReason, + } } - k.kcpEventInput <- &KcpEvent{ - ConvId: convId, - EventId: KcpConnForceClose, - EventMessage: connCtrlMsg.KickReason, + case mq.MsgTypeServer: + serverMsg := netMsg.ServerMsg + switch netMsg.EventId { + case mq.ServerUserGsChangeNotify: + convId, exist := userIdConvMap[serverMsg.UserId] + if !exist { + logger.Error("can not find convId by userId") + continue + } + session := convSessionMap[convId] + if session == nil { + logger.Error("session is nil, convId: %v", convId) + return + } + session.gsServerAppId = serverMsg.GameServerAppId + session.fightServerAppId = "" + session.changeGameServer = true + session.joinHostUserId = serverMsg.JoinHostUserId + // 网关代发登录请求到新的GS + gameMsg := new(mq.GameMsg) + gameMsg.UserId = serverMsg.UserId + gameMsg.CmdId = cmd.PlayerLoginReq + gameMsg.ClientSeq = 0 + gameMsg.PayloadMessage = new(proto.PlayerLoginReq) + k.messageQueue.SendToGs(session.gsServerAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeGame, + EventId: mq.NormalMsg, + GameMsg: gameMsg, + }) } } } diff --git a/gate/net/kcp_connect_manager.go b/gate/net/kcp_connect_manager.go index f276005b..3c60c2e6 100644 --- a/gate/net/kcp_connect_manager.go +++ b/gate/net/kcp_connect_manager.go @@ -117,13 +117,18 @@ func (k *KcpConnectManager) acceptHandle(listener *kcp.Listener) { logger.Debug("client connect, convId: %v", convId) kcpRawSendChan := make(chan *ProtoMsg, 1000) session := &Session{ - conn: conn, - connState: ConnEst, - userId: 0, - kcpRawSendChan: kcpRawSendChan, - seed: 0, - xorKey: k.dispatchKey, - changeXorKeyFin: false, + conn: conn, + connState: ConnEst, + userId: 0, + kcpRawSendChan: kcpRawSendChan, + seed: 0, + xorKey: k.dispatchKey, + changeXorKeyFin: false, + gsServerAppId: "", + fightServerAppId: "", + pathfindingServerAppId: "", + changeGameServer: false, + joinHostUserId: 0, } go k.recvHandle(session) go k.sendHandle(session) @@ -221,6 +226,8 @@ type Session struct { gsServerAppId string fightServerAppId string pathfindingServerAppId string + changeGameServer bool + joinHostUserId uint32 } // 接收 @@ -301,14 +308,20 @@ func (k *KcpConnectManager) sendHandle(session *Session) { if protoMsg.CmdId == cmd.PlayerLoginRsp { logger.Debug("session active, convId: %v", convId) session.connState = ConnActive - // 通知GS玩家战斗服务器的appid - connCtrlMsg := new(mq.ConnCtrlMsg) - connCtrlMsg.UserId = session.userId - connCtrlMsg.FightServerAppId = session.fightServerAppId + // 通知GS玩家各个服务器的appid + serverMsg := new(mq.ServerMsg) + serverMsg.UserId = session.userId + if session.changeGameServer { + serverMsg.JoinHostUserId = session.joinHostUserId + session.changeGameServer = false + session.joinHostUserId = 0 + } else { + serverMsg.FightServerAppId = session.fightServerAppId + } k.messageQueue.SendToGs(session.gsServerAppId, &mq.NetMsg{ - MsgType: mq.MsgTypeConnCtrl, - EventId: mq.FightServerSelectNotify, - ConnCtrlMsg: connCtrlMsg, + MsgType: mq.MsgTypeServer, + EventId: mq.ServerAppidBindNotify, + ServerMsg: serverMsg, }) } } diff --git a/gs/game/command_manager.go b/gs/game/command_manager.go index 164fc96d..5c48bc12 100644 --- a/gs/game/command_manager.go +++ b/gs/game/command_manager.go @@ -48,7 +48,7 @@ func NewCommandManager() *CommandManager { // 创建AI世界 GAME_MANAGER.OnRegOk(false, &proto.SetPlayerBornDataReq{AvatarId: 10000007, NickName: "System"}, 1, 0, "") - GAME_MANAGER.FightServerSelectNotify(1, "") + GAME_MANAGER.ServerAppidBindNotify(1, "", 0) r.system = USER_MANAGER.GetOnlineUser(1) r.system.DbState = model.DbNormal r.system.SceneLoadState = model.SceneEnterDone diff --git a/gs/game/game_manager.go b/gs/game/game_manager.go index 7724086f..0cee4e54 100644 --- a/gs/game/game_manager.go +++ b/gs/game/game_manager.go @@ -27,6 +27,8 @@ var WORLD_MANAGER *WorldManager = nil var TICK_MANAGER *TickManager = nil var COMMAND_MANAGER *CommandManager = nil +var SELF *model.Player + type GameManager struct { dao *dao.Dao messageQueue *mq.MessageQueue @@ -75,6 +77,10 @@ func (g *GameManager) gameMainLoop() { logger.Error("!!! GAME MAIN LOOP PANIC !!!") logger.Error("error: %v", err) logger.Error("stack: %v", logger.Stack()) + logger.Error("user: %v", SELF) + if SELF != nil { + GAME_MANAGER.DisconnectPlayer(SELF.PlayerID, kcp.EnetServerKick) + } } }() intervalTime := time.Second.Nanoseconds() * 60 diff --git a/gs/game/route_manager.go b/gs/game/route_manager.go index f7b23271..602eccfa 100644 --- a/gs/game/route_manager.go +++ b/gs/game/route_manager.go @@ -43,7 +43,9 @@ func (r *RouteManager) doRoute(cmdId uint16, userId uint32, clientSeq uint32, pa return } player.ClientSeq = clientSeq + SELF = player handlerFunc(player, payloadMsg) + SELF = nil } func (r *RouteManager) InitRoute() { @@ -116,6 +118,7 @@ func (r *RouteManager) InitRoute() { r.registerRouter(cmd.CreateVehicleReq, GAME_MANAGER.CreateVehicleReq) r.registerRouter(cmd.VehicleInteractReq, GAME_MANAGER.VehicleInteractReq) r.registerRouter(cmd.SceneEntityDrownReq, GAME_MANAGER.SceneEntityDrownReq) + r.registerRouter(cmd.GetOnlinePlayerInfoReq, GAME_MANAGER.GetOnlinePlayerInfoReq) } func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) { @@ -149,8 +152,19 @@ func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) { GAME_MANAGER.ClientRttNotify(connCtrlMsg.UserId, connCtrlMsg.ClientRtt) case mq.ClientTimeNotify: GAME_MANAGER.ClientTimeNotify(connCtrlMsg.UserId, connCtrlMsg.ClientTime) - case mq.FightServerSelectNotify: - GAME_MANAGER.FightServerSelectNotify(connCtrlMsg.UserId, connCtrlMsg.FightServerAppId) + } + case mq.MsgTypeServer: + serverMsg := netMsg.ServerMsg + switch netMsg.EventId { + case mq.ServerUserOnlineStateChangeNotify: + logger.Debug("remote user online state change, uid: %v, online: %v", serverMsg.UserId, serverMsg.IsOnline) + USER_MANAGER.SetRemoteUserOnlineState(serverMsg.UserId, serverMsg.IsOnline, netMsg.OriginServerAppId) + case mq.ServerAppidBindNotify: + GAME_MANAGER.ServerAppidBindNotify(serverMsg.UserId, serverMsg.FightServerAppId, serverMsg.JoinHostUserId) + case mq.ServerGetUserBaseInfoReq: + GAME_MANAGER.ServerGetUserBaseInfoReq(serverMsg.UserBaseInfo, netMsg.OriginServerAppId) + case mq.ServerGetUserBaseInfoRsp: + GAME_MANAGER.ServerGetUserBaseInfoRsp(serverMsg.UserBaseInfo) } } } diff --git a/gs/game/user_common_handler.go b/gs/game/user_common_handler.go index d3334b16..af229599 100644 --- a/gs/game/user_common_handler.go +++ b/gs/game/user_common_handler.go @@ -3,6 +3,7 @@ package game import ( "time" + "hk4e/common/constant" "hk4e/common/mq" "hk4e/gs/model" "hk4e/pkg/logger" @@ -77,31 +78,6 @@ func (g *GameManager) ClientTimeNotify(userId uint32, clientTime uint32) { player.ClientTime = clientTime } -func (g *GameManager) FightServerSelectNotify(userId uint32, fightAppId string) { - player := USER_MANAGER.GetOnlineUser(userId) - if player == nil { - logger.Error("player is nil, uid: %v", userId) - return - } - logger.Debug("fight server select notify, uid: %v, fightAppId: %v", userId, fightAppId) - player.FightAppId = fightAppId - // 创建世界 - world := WORLD_MANAGER.CreateWorld(player) - GAME_MANAGER.messageQueue.SendToFight(fightAppId, &mq.NetMsg{ - MsgType: mq.MsgTypeFight, - EventId: mq.AddFightRoutine, - FightMsg: &mq.FightMsg{ - FightRoutineId: world.id, - GateServerAppId: player.GateAppId, - }, - }) - world.AddPlayer(player, player.SceneId) - player.WorldId = world.id - // 进入场景 - player.SceneLoadState = model.SceneNone - g.SendMsg(cmd.PlayerEnterSceneNotify, userId, player.ClientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_TYPE_SELF)) -} - func (g *GameManager) ServerAnnounceNotify(announceId uint32, announceMsg string) { for _, onlinePlayer := range USER_MANAGER.GetAllOnlineUserList() { now := uint32(time.Now().Unix()) @@ -146,3 +122,84 @@ func (g *GameManager) SetEntityClientDataNotify(player *model.Player, payloadMsg ntf := payloadMsg.(*proto.SetEntityClientDataNotify) g.SendMsg(cmd.SetEntityClientDataNotify, player.PlayerID, player.ClientSeq, ntf) } + +func (g *GameManager) ServerAppidBindNotify(userId uint32, fightAppId string, joinHostUserId uint32) { + player := USER_MANAGER.GetOnlineUser(userId) + if player == nil { + logger.Error("player is nil, uid: %v", userId) + return + } + if joinHostUserId != 0 { + hostPlayer := USER_MANAGER.GetOnlineUser(joinHostUserId) + g.HostEnterMpWorld(hostPlayer, player.PlayerID) + g.JoinOtherWorld(player, hostPlayer) + return + } + logger.Debug("server appid bind notify, uid: %v, fightAppId: %v", userId, fightAppId) + player.FightAppId = fightAppId + // 创建世界 + world := WORLD_MANAGER.CreateWorld(player) + GAME_MANAGER.messageQueue.SendToFight(fightAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeFight, + EventId: mq.AddFightRoutine, + FightMsg: &mq.FightMsg{ + FightRoutineId: world.id, + GateServerAppId: player.GateAppId, + }, + }) + world.AddPlayer(player, player.SceneId) + player.WorldId = world.id + // 进入场景 + player.SceneLoadState = model.SceneNone + g.SendMsg(cmd.PlayerEnterSceneNotify, userId, player.ClientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_TYPE_SELF)) +} + +func (g *GameManager) ServerGetUserBaseInfoReq(userBaseInfo *mq.UserBaseInfo, gsAppId string) { + player := USER_MANAGER.GetOnlineUser(userBaseInfo.UserId) + if player == nil { + logger.Error("player is nil, uid: %v", userBaseInfo.UserId) + return + } + world := WORLD_MANAGER.GetWorldByID(player.WorldId) + g.messageQueue.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerGetUserBaseInfoRsp, + ServerMsg: &mq.ServerMsg{ + UserBaseInfo: &mq.UserBaseInfo{ + OriginInfo: userBaseInfo.OriginInfo, + UserId: player.PlayerID, + Nickname: player.NickName, + PlayerLevel: player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_LEVEL], + MpSettingType: uint8(player.PropertiesMap[constant.PlayerPropertyConst.PROP_PLAYER_MP_SETTING_TYPE]), + NameCardId: player.NameCard, + Signature: player.Signature, + HeadImageId: player.HeadImage, + WorldPlayerNum: uint32(world.GetWorldPlayerNum()), + }, + }, + }) +} + +func (g *GameManager) ServerGetUserBaseInfoRsp(userBaseInfo *mq.UserBaseInfo) { + switch userBaseInfo.OriginInfo.CmdName { + case "GetOnlinePlayerInfoReq": + player := USER_MANAGER.GetOnlineUser(userBaseInfo.OriginInfo.UserId) + if player == nil { + logger.Error("player is nil, uid: %v", userBaseInfo.OriginInfo.UserId) + return + } + g.SendMsg(cmd.GetOnlinePlayerInfoRsp, player.PlayerID, player.ClientSeq, &proto.GetOnlinePlayerInfoRsp{ + TargetUid: userBaseInfo.UserId, + TargetPlayerInfo: &proto.OnlinePlayerInfo{ + Uid: userBaseInfo.UserId, + Nickname: userBaseInfo.Nickname, + PlayerLevel: userBaseInfo.PlayerLevel, + MpSettingType: proto.MpSettingType(userBaseInfo.MpSettingType), + NameCardId: userBaseInfo.NameCardId, + Signature: userBaseInfo.Signature, + ProfilePicture: &proto.ProfilePicture{AvatarId: userBaseInfo.HeadImageId}, + CurPlayerNumInWorld: userBaseInfo.WorldPlayerNum, + }, + }) + } +} diff --git a/gs/game/user_login.go b/gs/game/user_login.go index fc0351e9..8b59b236 100644 --- a/gs/game/user_login.go +++ b/gs/game/user_login.go @@ -4,6 +4,7 @@ import ( "time" "hk4e/common/constant" + "hk4e/common/mq" gdc "hk4e/gs/config" "hk4e/gs/model" "hk4e/pkg/logger" @@ -59,6 +60,15 @@ func (g *GameManager) OnLoginOk(userId uint32, player *model.Player, clientSeq u player.AbilityInvokeHandler = model.NewInvokeHandler[proto.AbilityInvokeEntry]() g.LoginNotify(userId, player, clientSeq) + + g.messageQueue.SendToAll(&mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserOnlineStateChangeNotify, + ServerMsg: &mq.ServerMsg{ + UserId: userId, + IsOnline: true, + }, + }) } func (g *GameManager) OnReg(userId uint32, clientSeq uint32, gateAppId string, payloadMsg pb.Message) { @@ -111,6 +121,15 @@ func (g *GameManager) OnUserOffline(userId uint32) { player.Online = false player.TotalOnlineTime += uint32(time.Now().UnixMilli()) - player.OnlineTime USER_MANAGER.DeleteUser(player) + + g.messageQueue.SendToAll(&mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserOnlineStateChangeNotify, + ServerMsg: &mq.ServerMsg{ + UserId: userId, + IsOnline: false, + }, + }) } func (g *GameManager) LoginNotify(userId uint32, player *model.Player, clientSeq uint32) { diff --git a/gs/game/user_manager.go b/gs/game/user_manager.go index 8eb2de62..84bc96ab 100644 --- a/gs/game/user_manager.go +++ b/gs/game/user_manager.go @@ -15,9 +15,10 @@ type SaveUserData struct { } type UserManager struct { - dao *dao.Dao - playerMap map[uint32]*model.Player - saveUserChan chan *SaveUserData + dao *dao.Dao + playerMap map[uint32]*model.Player + saveUserChan chan *SaveUserData + remotePlayerMap map[uint32]string // 远程玩家 key:userId value:玩家所在gs的appid } func NewUserManager(dao *dao.Dao) (r *UserManager) { @@ -25,9 +26,36 @@ func NewUserManager(dao *dao.Dao) (r *UserManager) { r.dao = dao r.playerMap = make(map[uint32]*model.Player) r.saveUserChan = make(chan *SaveUserData) + r.remotePlayerMap = make(map[uint32]string) return r } +func (u *UserManager) GetRemoteUserOnlineState(userId uint32) bool { + _, exist := u.remotePlayerMap[userId] + if !exist { + return false + } else { + return true + } +} + +func (u *UserManager) GetRemoteUserGsAppId(userId uint32) string { + appId, exist := u.remotePlayerMap[userId] + if !exist { + return "" + } else { + return appId + } +} + +func (u *UserManager) SetRemoteUserOnlineState(userId uint32, isOnline bool, appId string) { + if isOnline { + u.remotePlayerMap[userId] = appId + } else { + delete(u.remotePlayerMap, userId) + } +} + func (u *UserManager) GetUserOnlineState(userId uint32) bool { player, exist := u.playerMap[userId] if !exist { @@ -119,6 +147,14 @@ func (u *UserManager) loadUserFromDb(userId uint32) *model.Player { return player } +func (u *UserManager) saveUserToDb(player *model.Player) { + err := u.dao.UpdatePlayer(player) + if err != nil { + logger.Error("update player error: %v", err) + return + } +} + func (u *UserManager) AddUser(player *model.Player) { if player == nil { return diff --git a/gs/game/user_multiplayer.go b/gs/game/user_multiplayer.go index d2c589ad..977c0ef9 100644 --- a/gs/game/user_multiplayer.go +++ b/gs/game/user_multiplayer.go @@ -5,6 +5,7 @@ import ( "hk4e/common/constant" "hk4e/common/mq" + "hk4e/gate/kcp" "hk4e/gs/model" "hk4e/pkg/logger" "hk4e/pkg/object" @@ -14,6 +15,8 @@ import ( pb "google.golang.org/protobuf/proto" ) +// 进入世界 + func (g *GameManager) PlayerApplyEnterMpReq(player *model.Player, payloadMsg pb.Message) { logger.Debug("user apply enter world, uid: %v", player.PlayerID) req := payloadMsg.(*proto.PlayerApplyEnterMpReq) @@ -51,6 +54,74 @@ func (g *GameManager) PlayerApplyEnterMpResultReq(player *model.Player, payloadM g.UserDealEnterWorld(player, applyUid, isAgreed) } +func (g *GameManager) JoinPlayerSceneReq(player *model.Player, payloadMsg pb.Message) { + logger.Debug("user join player scene, uid: %v", player.PlayerID) + req := payloadMsg.(*proto.JoinPlayerSceneReq) + + joinPlayerSceneRsp := new(proto.JoinPlayerSceneRsp) + joinPlayerSceneRsp.Retcode = int32(proto.Retcode_RET_JOIN_OTHER_WAIT) + g.SendMsg(cmd.JoinPlayerSceneRsp, player.PlayerID, player.ClientSeq, joinPlayerSceneRsp) + + world := WORLD_MANAGER.GetWorldByID(player.WorldId) + g.UserWorldRemovePlayer(world, player) + + g.SendMsg(cmd.LeaveWorldNotify, player.PlayerID, player.ClientSeq, new(proto.LeaveWorldNotify)) + + hostPlayer := USER_MANAGER.GetOnlineUser(req.TargetUid) + if hostPlayer == nil { + // 要加入的世界属于非本地玩家 + if !USER_MANAGER.GetRemoteUserOnlineState(req.TargetUid) { + // 全服不存在该在线玩家 + logger.Error("target user not online in any game server, uid: %v", req.TargetUid) + g.DisconnectPlayer(player.PlayerID, kcp.EnetServerKick) + return + } + // 走玩家在线跨服迁移流程 + g.OnUserOffline(player.PlayerID) + // TODO 改成异步写入数据库 + USER_MANAGER.saveUserToDb(player) + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(req.TargetUid) + g.messageQueue.SendToGate(player.GateAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerUserGsChangeNotify, + ServerMsg: &mq.ServerMsg{ + UserId: player.PlayerID, + GameServerAppId: gsAppId, + JoinHostUserId: req.TargetUid, + }, + }) + return + } + + g.JoinOtherWorld(player, hostPlayer) +} + +func (g *GameManager) JoinOtherWorld(player *model.Player, hostPlayer *model.Player) { + hostWorld := WORLD_MANAGER.GetWorldByID(hostPlayer.WorldId) + _, exist := hostWorld.waitEnterPlayerMap[player.PlayerID] + if !exist { + return + } + + if hostPlayer.SceneLoadState == model.SceneEnterDone { + delete(hostWorld.waitEnterPlayerMap, player.PlayerID) + player.Pos.X = hostPlayer.Pos.X + player.Pos.Y = hostPlayer.Pos.Y + player.Pos.Z = hostPlayer.Pos.Z + player.Rot.X = hostPlayer.Rot.X + player.Rot.Y = hostPlayer.Rot.Y + player.Rot.Z = hostPlayer.Rot.Z + player.SceneId = hostPlayer.SceneId + + g.UserWorldAddPlayer(hostWorld, player) + + player.SceneLoadState = model.SceneNone + g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_TYPE_OTHER)) + } +} + +// 退出世界 + func (g *GameManager) PlayerGetForceQuitBanInfoReq(player *model.Player, payloadMsg pb.Message) { logger.Debug("user get world exit ban info, uid: %v", player.PlayerID) ok := true @@ -122,44 +193,6 @@ func (g *GameManager) SceneKickPlayerReq(player *model.Player, payloadMsg pb.Mes g.SendMsg(cmd.SceneKickPlayerRsp, player.PlayerID, player.ClientSeq, sceneKickPlayerRsp) } -func (g *GameManager) JoinPlayerSceneReq(player *model.Player, payloadMsg pb.Message) { - logger.Debug("user join player scene, uid: %v", player.PlayerID) - req := payloadMsg.(*proto.JoinPlayerSceneReq) - hostPlayer := USER_MANAGER.GetOnlineUser(req.TargetUid) - hostWorld := WORLD_MANAGER.GetWorldByID(hostPlayer.WorldId) - _, exist := hostWorld.waitEnterPlayerMap[player.PlayerID] - if !exist { - return - } - - joinPlayerSceneRsp := new(proto.JoinPlayerSceneRsp) - joinPlayerSceneRsp.Retcode = int32(proto.Retcode_RET_JOIN_OTHER_WAIT) - g.SendMsg(cmd.JoinPlayerSceneRsp, player.PlayerID, player.ClientSeq, joinPlayerSceneRsp) - - world := WORLD_MANAGER.GetWorldByID(player.WorldId) - g.UserWorldRemovePlayer(world, player) - - g.SendMsg(cmd.LeaveWorldNotify, player.PlayerID, player.ClientSeq, new(proto.LeaveWorldNotify)) - - // g.LoginNotify(player.PlayerID, player, 0) - - if hostPlayer.SceneLoadState == model.SceneEnterDone { - delete(hostWorld.waitEnterPlayerMap, player.PlayerID) - player.Pos.X = hostPlayer.Pos.X - player.Pos.Y = hostPlayer.Pos.Y - player.Pos.Z = hostPlayer.Pos.Z - player.Rot.X = hostPlayer.Rot.X - player.Rot.Y = hostPlayer.Rot.Y - player.Rot.Z = hostPlayer.Rot.Z - player.SceneId = hostPlayer.SceneId - - g.UserWorldAddPlayer(hostWorld, player) - - player.SceneLoadState = model.SceneNone - g.SendMsg(cmd.PlayerEnterSceneNotify, player.PlayerID, player.ClientSeq, g.PacketPlayerEnterSceneNotifyLogin(player, proto.EnterType_ENTER_TYPE_OTHER)) - } -} - func (g *GameManager) UserApplyEnterWorld(player *model.Player, targetUid uint32) bool { targetPlayer := USER_MANAGER.GetOnlineUser(targetUid) if targetPlayer == nil { @@ -215,11 +248,14 @@ func (g *GameManager) UserDealEnterWorld(hostPlayer *model.Player, otherUid uint } g.SendMsg(cmd.PlayerApplyEnterMpResultNotify, otherPlayer.PlayerID, otherPlayer.ClientSeq, playerApplyEnterMpResultNotify) - if !agree { - return + if agree { + g.HostEnterMpWorld(hostPlayer, otherPlayer.PlayerID) } +} + +func (g *GameManager) HostEnterMpWorld(hostPlayer *model.Player, otherPlayerId uint32) { world := WORLD_MANAGER.GetWorldByID(hostPlayer.WorldId) - world.waitEnterPlayerMap[otherPlayer.PlayerID] = time.Now().UnixMilli() + world.waitEnterPlayerMap[otherPlayerId] = time.Now().UnixMilli() if world.multiplayer { return } @@ -250,7 +286,7 @@ func (g *GameManager) UserDealEnterWorld(hostPlayer *model.Player, otherUid uint guestBeginEnterSceneNotify := &proto.GuestBeginEnterSceneNotify{ SceneId: hostPlayer.SceneId, - Uid: otherPlayer.PlayerID, + Uid: otherPlayerId, } g.SendMsg(cmd.GuestBeginEnterSceneNotify, hostPlayer.PlayerID, hostPlayer.ClientSeq, guestBeginEnterSceneNotify) diff --git a/gs/game/user_social.go b/gs/game/user_social.go index 02f4e5d7..657422cd 100644 --- a/gs/game/user_social.go +++ b/gs/game/user_social.go @@ -6,6 +6,7 @@ import ( "unicode/utf8" "hk4e/common/constant" + "hk4e/common/mq" "hk4e/gs/model" "hk4e/pkg/logger" "hk4e/pkg/object" @@ -335,6 +336,46 @@ func (g *GameManager) GetOnlinePlayerListReq(player *model.Player, payloadMsg pb g.SendMsg(cmd.GetOnlinePlayerListRsp, player.PlayerID, player.ClientSeq, getOnlinePlayerListRsp) } +func (g *GameManager) GetOnlinePlayerInfoReq(player *model.Player, payloadMsg pb.Message) { + logger.Debug("user get online player info, uid: %v", player.PlayerID) + req := payloadMsg.(*proto.GetOnlinePlayerInfoReq) + targetUid, ok := req.PlayerId.(*proto.GetOnlinePlayerInfoReq_TargetUid) + if !ok { + return + } + + // TODO 删除我 + g.JoinPlayerSceneReq(player, &proto.JoinPlayerSceneReq{ + TargetUid: targetUid.TargetUid, + }) + + if USER_MANAGER.GetUserOnlineState(targetUid.TargetUid) { + g.SendMsg(cmd.GetOnlinePlayerInfoRsp, player.PlayerID, player.ClientSeq, &proto.GetOnlinePlayerInfoRsp{ + TargetUid: targetUid.TargetUid, + TargetPlayerInfo: g.PacketOnlinePlayerInfo(player), + }) + return + } + if USER_MANAGER.GetRemoteUserOnlineState(targetUid.TargetUid) { + gsAppId := USER_MANAGER.GetRemoteUserGsAppId(targetUid.TargetUid) + g.messageQueue.SendToGs(gsAppId, &mq.NetMsg{ + MsgType: mq.MsgTypeServer, + EventId: mq.ServerGetUserBaseInfoReq, + ServerMsg: &mq.ServerMsg{ + UserBaseInfo: &mq.UserBaseInfo{ + OriginInfo: &mq.OriginInfo{ + CmdName: "GetOnlinePlayerInfoReq", + UserId: player.PlayerID, + }, + UserId: targetUid.TargetUid, + }, + }, + }) + return + } + g.CommonRetError(cmd.GetOnlinePlayerInfoRsp, player, &proto.GetOnlinePlayerInfoRsp{}, proto.Retcode_RET_PLAYER_NOT_ONLINE) +} + func (g *GameManager) PacketOnlinePlayerInfo(player *model.Player) *proto.OnlinePlayerInfo { world := WORLD_MANAGER.GetWorldByID(player.WorldId) onlinePlayerInfo := &proto.OnlinePlayerInfo{ diff --git a/protocol/cmd/cmd_id_proto_obj_map.go b/protocol/cmd/cmd_id_proto_obj_map.go index e9c144cf..8237b792 100644 --- a/protocol/cmd/cmd_id_proto_obj_map.go +++ b/protocol/cmd/cmd_id_proto_obj_map.go @@ -199,6 +199,8 @@ func (c *CmdProtoMap) registerAllMessage() { c.registerMessage(PlayerChatReq, &proto.PlayerChatReq{}) // 多人聊天消息发送请求 c.registerMessage(PlayerChatRsp, &proto.PlayerChatRsp{}) // 多人聊天消息发送响应 c.registerMessage(PlayerChatNotify, &proto.PlayerChatNotify{}) // 多人聊天消息通知 + c.registerMessage(GetOnlinePlayerInfoReq, &proto.GetOnlinePlayerInfoReq{}) // 在线玩家信息请求 + c.registerMessage(GetOnlinePlayerInfoRsp, &proto.GetOnlinePlayerInfoRsp{}) // 在线玩家信息响应 // 卡池 c.registerMessage(GetGachaInfoReq, &proto.GetGachaInfoReq{}) // 卡池获取请求