实现跨服加入世界&玩家跨服在线迁移功能(部分完成)

This commit is contained in:
flswld
2022-12-25 20:09:19 +08:00
parent 9feeb4eafa
commit c7ba154ab4
15 changed files with 437 additions and 136 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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)
}
}
}

View File

@@ -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,
},
})
}
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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)

View File

@@ -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{