优化登录、离线、服务器重启等会话连接问题

This commit is contained in:
huangxiaolei
2022-12-21 14:25:14 +08:00
parent a288892f26
commit bfff798470
14 changed files with 348 additions and 307 deletions

View File

@@ -61,7 +61,7 @@ func (m *MessageQueue) recvHandler() {
switch netMsg.MsgType { switch netMsg.MsgType {
case MsgTypeGame: case MsgTypeGame:
gameMsg := netMsg.GameMsg gameMsg := netMsg.GameMsg
if netMsg.EventId == NormalMsg || netMsg.EventId == UserRegNotify { if netMsg.EventId == NormalMsg {
// protobuf PayloadMessage // protobuf PayloadMessage
payloadMessage := m.cmdProtoMap.GetProtoObjByCmdId(gameMsg.CmdId) payloadMessage := m.cmdProtoMap.GetProtoObjByCmdId(gameMsg.CmdId)
if payloadMessage == nil { if payloadMessage == nil {
@@ -76,6 +76,7 @@ func (m *MessageQueue) recvHandler() {
gameMsg.PayloadMessage = payloadMessage gameMsg.PayloadMessage = payloadMessage
} }
case MsgTypeFight: case MsgTypeFight:
case MsgTypeConnCtrl:
} }
m.netMsgOutput <- netMsg m.netMsgOutput <- netMsg
} }
@@ -97,6 +98,7 @@ func (m *MessageQueue) sendHandler() {
gameMsg.PayloadMessageData = payloadMessageData gameMsg.PayloadMessageData = payloadMessageData
} }
case MsgTypeFight: case MsgTypeFight:
case MsgTypeConnCtrl:
} }
// msgpack NetMsg // msgpack NetMsg
netMsgData, err := msgpack.Marshal(netMsg) netMsgData, err := msgpack.Marshal(netMsg)

View File

@@ -5,35 +5,45 @@ import pb "google.golang.org/protobuf/proto"
const ( const (
MsgTypeGame = iota MsgTypeGame = iota
MsgTypeFight MsgTypeFight
MsgTypeConnCtrl
) )
type NetMsg struct { type NetMsg struct {
MsgType uint8 `msgpack:"MsgType"` MsgType uint8 `msgpack:"MsgType"`
EventId uint16 `msgpack:"EventId"` EventId uint16 `msgpack:"EventId"`
Topic string `msgpack:"-"` Topic string `msgpack:"-"`
GameMsg *GameMsg `msgpack:"GameMsg"` GameMsg *GameMsg `msgpack:"GameMsg"`
FightMsg *FightMsg `msgpack:"FightMsg"` FightMsg *FightMsg `msgpack:"FightMsg"`
ConnCtrlMsg *ConnCtrlMsg `msgpack:"ConnCtrlMsg"`
} }
const ( const (
NormalMsg = iota NormalMsg = iota
UserRegNotify
UserLoginNotify
UserOfflineNotify UserOfflineNotify
ClientRttNotify
ClientTimeNotify
) )
type GameMsg struct { type GameMsg struct {
UserId uint32 `msgpack:"UserId"` UserId uint32 `msgpack:"UserId"`
CmdId uint16 `msgpack:"CmdId"` CmdId uint16 `msgpack:"CmdId"`
ClientSeq uint32 `msgpack:"ClientSeq"` ClientSeq uint32 `msgpack:"ClientSeq"`
ClientRtt uint32 `msgpack:"ClientRtt"`
ClientTime uint32 `msgpack:"ClientTime"`
PayloadMessage pb.Message `msgpack:"-"` PayloadMessage pb.Message `msgpack:"-"`
PayloadMessageData []byte `msgpack:"PayloadMessageData"` PayloadMessageData []byte `msgpack:"PayloadMessageData"`
} }
const (
ClientRttNotify = iota
ClientTimeNotify
KickPlayerNotify
)
type ConnCtrlMsg struct {
UserId uint32 `msgpack:"UserId"`
ClientRtt uint32 `msgpack:"ClientRtt"`
ClientTime uint32 `msgpack:"ClientTime"`
KickUserId uint32 `msgpack:"KickUserId"`
KickReason uint32 `msgpack:"KickReason"`
}
const ( const (
AddFightRoutine = iota AddFightRoutine = iota
DelFightRoutine DelFightRoutine

View File

@@ -24,6 +24,7 @@ func Run(ctx context.Context, configFile string) error {
connectManager := net.NewKcpConnectManager(messageQueue) connectManager := net.NewKcpConnectManager(messageQueue)
connectManager.Start() connectManager.Start()
defer connectManager.Stop()
go func() { go func() {
outputChan := connectManager.GetKcpEventOutputChan() outputChan := connectManager.GetKcpEventOutputChan()

View File

@@ -22,26 +22,23 @@ import (
) )
const ( const (
ConnWaitToken = iota ConnEst = iota
ConnWaitLogin ConnActive
ConnAlive
ConnClose ConnClose
) )
// 发送消息到GS // 发送消息到GS
func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session) { func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session) {
userId := session.userId userId := session.userId
headMeta := session.headMeta
connState := session.connState connState := session.connState
if protoMsg.HeadMessage == nil { if protoMsg.HeadMessage == nil {
logger.Error("recv null head msg: %v", protoMsg) logger.Error("recv null head msg: %v", protoMsg)
} }
headMeta.seq = protoMsg.HeadMessage.ClientSequenceId
// gate本地处理的请求 // gate本地处理的请求
switch protoMsg.CmdId { switch protoMsg.CmdId {
case cmd.GetPlayerTokenReq: case cmd.GetPlayerTokenReq:
// 获取玩家token请求 // 获取玩家token请求
if connState != ConnWaitToken { if connState != ConnEst {
return return
} }
getPlayerTokenReq := protoMsg.PayloadMessage.(*proto.GetPlayerTokenReq) getPlayerTokenReq := protoMsg.PayloadMessage.(*proto.GetPlayerTokenReq)
@@ -56,51 +53,9 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session)
rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId) rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
rsp.PayloadMessage = getPlayerTokenRsp rsp.PayloadMessage = getPlayerTokenRsp
k.localMsgOutput <- rsp k.localMsgOutput <- rsp
case cmd.PlayerLoginReq:
// 玩家登录请求
if connState != ConnWaitLogin {
return
}
playerLoginReq := protoMsg.PayloadMessage.(*proto.PlayerLoginReq)
playerLoginRsp := k.playerLogin(playerLoginReq, session)
if playerLoginRsp == nil {
return
}
// 返回数据到客户端
rsp := new(ProtoMsg)
rsp.ConvId = protoMsg.ConvId
rsp.CmdId = cmd.PlayerLoginRsp
rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
rsp.PayloadMessage = playerLoginRsp
k.localMsgOutput <- rsp
// 登录成功 通知GS初始化相关数据
gameMsg := new(mq.GameMsg)
gameMsg.UserId = userId
gameMsg.ClientSeq = headMeta.seq
k.messageQueue.SendToGs("1", &mq.NetMsg{
MsgType: mq.MsgTypeGame,
EventId: mq.UserLoginNotify,
GameMsg: gameMsg,
})
logger.Info("send to gs user login ok, ConvId: %v, UserId: %v", protoMsg.ConvId, gameMsg.UserId)
case cmd.SetPlayerBornDataReq:
// 玩家注册请求
if connState != ConnAlive {
return
}
gameMsg := new(mq.GameMsg)
gameMsg.UserId = userId
gameMsg.CmdId = cmd.SetPlayerBornDataReq
gameMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId
gameMsg.PayloadMessage = protoMsg.PayloadMessage
k.messageQueue.SendToGs("1", &mq.NetMsg{
MsgType: mq.MsgTypeGame,
EventId: mq.UserRegNotify,
GameMsg: gameMsg,
})
case cmd.PlayerForceExitReq: case cmd.PlayerForceExitReq:
// 玩家退出游戏请求 // 玩家退出游戏请求
if connState != ConnAlive { if connState != ConnActive {
return return
} }
k.kcpEventInput <- &KcpEvent{ k.kcpEventInput <- &KcpEvent{
@@ -110,13 +65,9 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session)
} }
case cmd.PingReq: case cmd.PingReq:
// ping请求 // ping请求
if connState != ConnAlive {
return
}
pingReq := protoMsg.PayloadMessage.(*proto.PingReq) pingReq := protoMsg.PayloadMessage.(*proto.PingReq)
logger.Debug("user ping req, data: %v", pingReq.String()) logger.Debug("user ping req, data: %v", pingReq.String())
// 返回数据到客户端 // 返回数据到客户端
// TODO 记录客户端最后一次ping时间做超时下线处理
pingRsp := new(proto.PingRsp) pingRsp := new(proto.PingRsp)
pingRsp.ClientTime = pingReq.ClientTime pingRsp.ClientTime = pingReq.ClientTime
rsp := new(ProtoMsg) rsp := new(ProtoMsg)
@@ -125,32 +76,45 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session)
rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId) rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
rsp.PayloadMessage = pingRsp rsp.PayloadMessage = pingRsp
k.localMsgOutput <- rsp k.localMsgOutput <- rsp
// 通知GS玩家客户端的本地时钟
gameMsg := new(mq.GameMsg)
gameMsg.UserId = userId
gameMsg.ClientTime = pingReq.ClientTime
k.messageQueue.SendToGs("1", &mq.NetMsg{
MsgType: mq.MsgTypeGame,
EventId: mq.ClientTimeNotify,
GameMsg: gameMsg,
})
// RTT
logger.Debug("convId: %v, RTO: %v, SRTT: %v, RTTVar: %v", protoMsg.ConvId, session.conn.GetRTO(), session.conn.GetSRTT(), session.conn.GetSRTTVar()) logger.Debug("convId: %v, RTO: %v, SRTT: %v, RTTVar: %v", protoMsg.ConvId, session.conn.GetRTO(), session.conn.GetSRTT(), session.conn.GetSRTTVar())
rtt := session.conn.GetSRTT() if connState != ConnActive {
// 通知GS玩家客户端往返时延
gameMsg = new(mq.GameMsg)
gameMsg.UserId = userId
gameMsg.ClientRtt = uint32(rtt)
k.messageQueue.SendToGs("1", &mq.NetMsg{
MsgType: mq.MsgTypeGame,
EventId: mq.ClientRttNotify,
GameMsg: gameMsg,
})
default:
// 未登录禁止访问
if connState != ConnAlive {
return return
} }
// 通知GS玩家客户端的本地时钟
connCtrlMsg := new(mq.ConnCtrlMsg)
connCtrlMsg.UserId = userId
connCtrlMsg.ClientTime = pingReq.ClientTime
k.messageQueue.SendToGs("1", &mq.NetMsg{
MsgType: mq.MsgTypeConnCtrl,
EventId: mq.ClientTimeNotify,
ConnCtrlMsg: connCtrlMsg,
})
// 通知GS玩家客户端往返时延
rtt := session.conn.GetSRTT()
connCtrlMsg = new(mq.ConnCtrlMsg)
connCtrlMsg.UserId = userId
connCtrlMsg.ClientRtt = uint32(rtt)
k.messageQueue.SendToGs("1", &mq.NetMsg{
MsgType: mq.MsgTypeConnCtrl,
EventId: mq.ClientRttNotify,
ConnCtrlMsg: connCtrlMsg,
})
default:
if connState != ConnActive && !(protoMsg.CmdId == cmd.PlayerLoginReq || protoMsg.CmdId == cmd.SetPlayerBornDataReq) {
logger.Error("conn not active so drop packet, cmdId: %v, userId: %v, convId: %v", protoMsg.CmdId, userId, protoMsg.ConvId)
return
}
// 转发到GS
gameMsg := new(mq.GameMsg)
gameMsg.UserId = userId
gameMsg.CmdId = protoMsg.CmdId
gameMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId
gameMsg.PayloadMessage = protoMsg.PayloadMessage
k.messageQueue.SendToGs("1", &mq.NetMsg{
MsgType: mq.MsgTypeGame,
EventId: mq.NormalMsg,
GameMsg: gameMsg,
})
// 转发到FIGHT // 转发到FIGHT
if protoMsg.CmdId == cmd.CombatInvocationsNotify { if protoMsg.CmdId == cmd.CombatInvocationsNotify {
gameMsg := new(mq.GameMsg) gameMsg := new(mq.GameMsg)
@@ -164,17 +128,6 @@ func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session)
GameMsg: gameMsg, GameMsg: gameMsg,
}) })
} }
// 转发到GS
gameMsg := new(mq.GameMsg)
gameMsg.UserId = userId
gameMsg.CmdId = protoMsg.CmdId
gameMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId
gameMsg.PayloadMessage = protoMsg.PayloadMessage
k.messageQueue.SendToGs("1", &mq.NetMsg{
MsgType: mq.MsgTypeGame,
EventId: mq.NormalMsg,
GameMsg: gameMsg,
})
} }
} }
@@ -208,26 +161,40 @@ func (k *KcpConnectManager) sendMsgHandle() {
case protoMsg := <-k.localMsgOutput: case protoMsg := <-k.localMsgOutput:
sendToClientFn(protoMsg) sendToClientFn(protoMsg)
case netMsg := <-k.messageQueue.GetNetMsg(): case netMsg := <-k.messageQueue.GetNetMsg():
if netMsg.MsgType != mq.MsgTypeGame { switch netMsg.MsgType {
logger.Error("recv unknown msg type from game server, msg type: %v", netMsg.MsgType) case mq.MsgTypeGame:
continue 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
}
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
}
k.kcpEventInput <- &KcpEvent{
ConvId: convId,
EventId: KcpConnForceClose,
EventMessage: connCtrlMsg.KickReason,
}
} }
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
}
protoMsg := new(ProtoMsg)
protoMsg.ConvId = convId
protoMsg.CmdId = gameMsg.CmdId
protoMsg.HeadMessage = k.getHeadMsg(gameMsg.ClientSeq)
protoMsg.PayloadMessage = gameMsg.PayloadMessage
sendToClientFn(protoMsg)
} }
} }
} }
@@ -276,15 +243,16 @@ func (k *KcpConnectManager) getPlayerToken(req *proto.GetPlayerTokenReq, session
oldSession := k.GetSessionByUserId(tokenVerifyRsp.PlayerID) oldSession := k.GetSessionByUserId(tokenVerifyRsp.PlayerID)
if oldSession != nil { if oldSession != nil {
// 顶号 // 顶号
kickFinishNotifyChan := make(chan bool)
k.kcpEventInput <- &KcpEvent{ k.kcpEventInput <- &KcpEvent{
ConvId: oldSession.conn.GetConv(), ConvId: oldSession.conn.GetConv(),
EventId: KcpConnForceClose, EventId: KcpConnRelogin,
EventMessage: uint32(kcp.EnetServerRelogin), EventMessage: kickFinishNotifyChan,
} }
<-kickFinishNotifyChan
} }
// 关联玩家uid和连接信息 // 关联玩家uid和连接信息
session.userId = tokenVerifyRsp.PlayerID session.userId = tokenVerifyRsp.PlayerID
session.connState = ConnWaitLogin
k.SetSession(session, session.conn.GetConv(), session.userId) k.SetSession(session, session.conn.GetConv(), session.userId)
k.createSessionChan <- session k.createSessionChan <- session
// 返回响应 // 返回响应
@@ -344,7 +312,6 @@ func (k *KcpConnectManager) getPlayerToken(req *proto.GetPlayerTokenReq, session
timeRand := random.GetTimeRand() timeRand := random.GetTimeRand()
serverSeedUint64 := timeRand.Uint64() serverSeedUint64 := timeRand.Uint64()
session.seed = serverSeedUint64 session.seed = serverSeedUint64
session.changeXorKey = true
seedUint64 := serverSeedUint64 ^ clientSeedUint64 seedUint64 := serverSeedUint64 ^ clientSeedUint64
seedBuf := new(bytes.Buffer) seedBuf := new(bytes.Buffer)
err = binary.Write(seedBuf, binary.BigEndian, seedUint64) err = binary.Write(seedBuf, binary.BigEndian, seedUint64)
@@ -369,27 +336,3 @@ func (k *KcpConnectManager) getPlayerToken(req *proto.GetPlayerTokenReq, session
} }
return rsp return rsp
} }
func (k *KcpConnectManager) playerLogin(req *proto.PlayerLoginReq, session *Session) (rsp *proto.PlayerLoginRsp) {
logger.Debug("player login, info: %v", req.String())
// TODO 验证token
session.connState = ConnAlive
// 返回响应
rsp = new(proto.PlayerLoginRsp)
rsp.IsUseAbilityHash = true
rsp.AbilityHashCode = -228935105
rsp.GameBiz = "hk4e_cn"
rsp.IsScOpen = false
rsp.RegisterCps = "taptap"
rsp.CountryCode = "CN"
rsp.Birthday = "2000-01-01"
rsp.TotalTickTime = 1185941.871788
rsp.ClientDataVersion = k.regionCurr.RegionInfo.ClientDataVersion
rsp.ClientSilenceDataVersion = k.regionCurr.RegionInfo.ClientSilenceDataVersion
rsp.ClientMd5 = k.regionCurr.RegionInfo.ClientDataMd5
rsp.ClientSilenceMd5 = k.regionCurr.RegionInfo.ClientSilenceDataMd5
rsp.ResVersionConfig = k.regionCurr.RegionInfo.ResVersionConfig
rsp.ClientVersionSuffix = k.regionCurr.RegionInfo.ClientVersionSuffix
rsp.ClientSilenceVersionSuffix = k.regionCurr.RegionInfo.ClientSilenceVersionSuffix
return rsp
}

View File

@@ -16,7 +16,6 @@ import (
"hk4e/pkg/logger" "hk4e/pkg/logger"
"hk4e/pkg/random" "hk4e/pkg/random"
"hk4e/protocol/cmd" "hk4e/protocol/cmd"
"hk4e/protocol/proto"
) )
const PacketFreqLimit = 1000 const PacketFreqLimit = 1000
@@ -35,7 +34,6 @@ type KcpConnectManager struct {
destroySessionChan chan *Session destroySessionChan chan *Session
// 密钥相关 // 密钥相关
dispatchKey []byte dispatchKey []byte
regionCurr *proto.QueryCurrRegionHttpRsp
signRsaKey []byte signRsaKey []byte
encRsaKeyMap map[string][]byte encRsaKeyMap map[string][]byte
} }
@@ -58,9 +56,6 @@ func NewKcpConnectManager(messageQueue *mq.MessageQueue) (r *KcpConnectManager)
func (k *KcpConnectManager) Start() { func (k *KcpConnectManager) Start() {
// 读取密钥相关文件 // 读取密钥相关文件
k.signRsaKey, k.encRsaKeyMap, _ = region.LoadRsaKey() k.signRsaKey, k.encRsaKeyMap, _ = region.LoadRsaKey()
// region
regionCurr, _, _ := region.InitRegion(config.CONF.Hk4e.KcpAddr, config.CONF.Hk4e.KcpPort)
k.regionCurr = regionCurr
// key // key
dispatchEc2bSeedRsp, err := httpclient.Get[controller.DispatchEc2bSeedRsp]("http://127.0.0.1:8080/dispatch/ec2b/seed", "") dispatchEc2bSeedRsp, err := httpclient.Get[controller.DispatchEc2bSeedRsp]("http://127.0.0.1:8080/dispatch/ec2b/seed", "")
if err != nil { if err != nil {
@@ -89,6 +84,11 @@ func (k *KcpConnectManager) Start() {
go k.acceptHandle(listener) go k.acceptHandle(listener)
} }
func (k *KcpConnectManager) Stop() {
k.closeAllKcpConn()
time.Sleep(time.Second * 3)
}
func (k *KcpConnectManager) acceptHandle(listener *kcp.Listener) { func (k *KcpConnectManager) acceptHandle(listener *kcp.Listener) {
logger.Debug("accept handle start") logger.Debug("accept handle start")
for { for {
@@ -107,16 +107,13 @@ func (k *KcpConnectManager) acceptHandle(listener *kcp.Listener) {
logger.Debug("client connect, convId: %v", convId) logger.Debug("client connect, convId: %v", convId)
kcpRawSendChan := make(chan *ProtoMsg, 1000) kcpRawSendChan := make(chan *ProtoMsg, 1000)
session := &Session{ session := &Session{
conn: conn, conn: conn,
connState: ConnWaitToken, connState: ConnEst,
userId: 0, userId: 0,
headMeta: &ClientHeadMeta{ kcpRawSendChan: kcpRawSendChan,
seq: 0, seed: 0,
}, xorKey: k.dispatchKey,
kcpRawSendChan: kcpRawSendChan, changeXorKeyFin: false,
seed: 0,
xorKey: k.dispatchKey,
changeXorKey: false,
} }
go k.recvHandle(session) go k.recvHandle(session)
go k.sendHandle(session) go k.sendHandle(session)
@@ -203,24 +200,19 @@ func (k *KcpConnectManager) enetHandle(listener *kcp.Listener) {
} }
} }
type ClientHeadMeta struct {
seq uint32
}
type Session struct { type Session struct {
conn *kcp.UDPSession conn *kcp.UDPSession
connState uint8 connState uint8
userId uint32 userId uint32
headMeta *ClientHeadMeta kcpRawSendChan chan *ProtoMsg
kcpRawSendChan chan *ProtoMsg seed uint64
seed uint64 xorKey []byte
xorKey []byte changeXorKeyFin bool
changeXorKey bool
} }
// 接收
func (k *KcpConnectManager) recvHandle(session *Session) { func (k *KcpConnectManager) recvHandle(session *Session) {
logger.Debug("recv handle start") logger.Debug("recv handle start")
// 接收
conn := session.conn conn := session.conn
convId := conn.GetConv() convId := conn.GetConv()
pktFreqLimitCounter := 0 pktFreqLimitCounter := 0
@@ -234,14 +226,6 @@ func (k *KcpConnectManager) recvHandle(session *Session) {
k.closeKcpConn(session, kcp.EnetServerKick) k.closeKcpConn(session, kcp.EnetServerKick)
break break
} }
if session.changeXorKey {
session.changeXorKey = false
keyBlock := random.NewKeyBlock(session.seed)
xorKey := keyBlock.XorKey()
key := make([]byte, 4096)
copy(key, xorKey[:])
session.xorKey = key
}
// 收包频率限制 // 收包频率限制
pktFreqLimitCounter++ pktFreqLimitCounter++
now := time.Now().UnixNano() now := time.Now().UnixNano()
@@ -267,9 +251,9 @@ func (k *KcpConnectManager) recvHandle(session *Session) {
} }
} }
// 发送
func (k *KcpConnectManager) sendHandle(session *Session) { func (k *KcpConnectManager) sendHandle(session *Session) {
logger.Debug("send handle start") logger.Debug("send handle start")
// 发送
conn := session.conn conn := session.conn
convId := conn.GetConv() convId := conn.GetConv()
for { for {
@@ -292,9 +276,33 @@ func (k *KcpConnectManager) sendHandle(session *Session) {
k.closeKcpConn(session, kcp.EnetServerKick) k.closeKcpConn(session, kcp.EnetServerKick)
break break
} }
if session.changeXorKeyFin == false && protoMsg.CmdId == cmd.GetPlayerTokenRsp {
logger.Debug("change session xor key, convId: %v", convId)
session.changeXorKeyFin = true
keyBlock := random.NewKeyBlock(session.seed)
xorKey := keyBlock.XorKey()
key := make([]byte, 4096)
copy(key, xorKey[:])
session.xorKey = key
}
if protoMsg.CmdId == cmd.PlayerLoginRsp {
logger.Debug("session active, convId: %v", convId)
session.connState = ConnActive
}
} }
} }
func (k *KcpConnectManager) forceCloseKcpConn(convId uint64, reason uint32) {
// 强制关闭某个连接
session := k.GetSessionByConvId(convId)
if session == nil {
logger.Error("session not exist, convId: %v", convId)
return
}
k.closeKcpConn(session, reason)
logger.Info("conn has been force close, convId: %v", convId)
}
func (k *KcpConnectManager) closeKcpConn(session *Session, enetType uint32) { func (k *KcpConnectManager) closeKcpConn(session *Session, enetType uint32) {
if session.connState == ConnClose { if session.connState == ConnClose {
return return
@@ -330,19 +338,14 @@ func (k *KcpConnectManager) closeKcpConn(session *Session, enetType uint32) {
} }
func (k *KcpConnectManager) closeAllKcpConn() { func (k *KcpConnectManager) closeAllKcpConn() {
closeConnList := make([]*kcp.UDPSession, 0) sessionList := make([]*Session, 0)
k.sessionMapLock.RLock() k.sessionMapLock.RLock()
for _, v := range k.sessionConvIdMap { for _, session := range k.sessionConvIdMap {
closeConnList = append(closeConnList, v.conn) sessionList = append(sessionList, session)
} }
k.sessionMapLock.RUnlock() k.sessionMapLock.RUnlock()
for _, v := range closeConnList { for _, session := range sessionList {
// 关闭连接 k.closeKcpConn(session, kcp.EnetServerShutdown)
v.SendEnetNotify(&kcp.Enet{
ConnType: kcp.ConnEnetFin,
EnetType: kcp.EnetServerShutdown,
})
_ = v.Close()
} }
} }

View File

@@ -11,6 +11,7 @@ const (
KcpConnForceClose = iota KcpConnForceClose = iota
KcpAllConnForceClose KcpAllConnForceClose
KcpGateOpenState KcpGateOpenState
KcpConnRelogin
KcpConnCloseNotify KcpConnCloseNotify
KcpConnEstNotify KcpConnEstNotify
KcpConnAddrChangeNotify KcpConnAddrChangeNotify
@@ -38,23 +39,12 @@ func (k *KcpConnectManager) eventHandle() {
logger.Info("kcp manager recv event, ConvId: %v, EventId: %v, EventMessage Type: %v", event.ConvId, event.EventId, reflect.TypeOf(event.EventMessage)) logger.Info("kcp manager recv event, ConvId: %v, EventId: %v, EventMessage Type: %v", event.ConvId, event.EventId, reflect.TypeOf(event.EventMessage))
switch event.EventId { switch event.EventId {
case KcpConnForceClose: case KcpConnForceClose:
// 强制关闭某个连接
session := k.GetSessionByConvId(event.ConvId)
if session == nil {
logger.Error("session not exist, convId: %v", event.ConvId)
continue
}
reason, ok := event.EventMessage.(uint32) reason, ok := event.EventMessage.(uint32)
if !ok { if !ok {
logger.Error("event KcpConnForceClose msg type error") logger.Error("event KcpConnForceClose msg type error")
continue return
} }
session.conn.SendEnetNotify(&kcp.Enet{ k.forceCloseKcpConn(event.ConvId, reason)
ConnType: kcp.ConnEnetFin,
EnetType: reason,
})
_ = session.conn.Close()
logger.Info("conn has been force close, convId: %v", event.ConvId)
case KcpAllConnForceClose: case KcpAllConnForceClose:
// 强制关闭所有连接 // 强制关闭所有连接
k.closeAllKcpConn() k.closeAllKcpConn()
@@ -70,6 +60,14 @@ func (k *KcpConnectManager) eventHandle() {
if openState == false { if openState == false {
k.closeAllKcpConn() k.closeAllKcpConn()
} }
case KcpConnRelogin:
kickFinishNotifyChan, ok := event.EventMessage.(chan bool)
if !ok {
logger.Error("event KcpConnRelogin msg type error")
continue
}
k.forceCloseKcpConn(event.ConvId, kcp.EnetServerRelogin)
kickFinishNotifyChan <- true
} }
} }
} }

View File

@@ -49,7 +49,6 @@ func Run(ctx context.Context, configFile string) error {
defer messageQueue.Close() defer messageQueue.Close()
gameManager := game.NewGameManager(db, messageQueue) gameManager := game.NewGameManager(db, messageQueue)
gameManager.Start()
defer gameManager.Stop() defer gameManager.Stop()
c := make(chan os.Signal, 1) c := make(chan os.Signal, 1)

View File

@@ -4,6 +4,7 @@ import (
"time" "time"
"hk4e/common/mq" "hk4e/common/mq"
"hk4e/gate/kcp"
"hk4e/gs/dao" "hk4e/gs/dao"
"hk4e/gs/model" "hk4e/gs/model"
"hk4e/pkg/alg" "hk4e/pkg/alg"
@@ -41,80 +42,104 @@ func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue) (r *GameManager
WORLD_MANAGER = NewWorldManager(r.snowflake) WORLD_MANAGER = NewWorldManager(r.snowflake)
TICK_MANAGER = NewTickManager() TICK_MANAGER = NewTickManager()
COMMAND_MANAGER = NewCommandManager() COMMAND_MANAGER = NewCommandManager()
r.run()
return r return r
} }
func (g *GameManager) Start() { func (g *GameManager) run() {
ROUTE_MANAGER.InitRoute() ROUTE_MANAGER.InitRoute()
USER_MANAGER.StartAutoSaveUser() USER_MANAGER.StartAutoSaveUser()
go func() { go g.gameMainLoopD()
intervalTime := time.Second.Nanoseconds() * 60 }
lastTime := time.Now().UnixNano()
routeCost := int64(0) func (g *GameManager) gameMainLoopD() {
tickCost := int64(0) for times := 1; times <= 1000; times++ {
localEventCost := int64(0) logger.Warn("start game main loop, times: %v", times)
commandCost := int64(0) g.gameMainLoop()
for { logger.Warn("game main loop stop")
now := time.Now().UnixNano() }
if now-lastTime > intervalTime { }
routeCost /= 1e6
tickCost /= 1e6 func (g *GameManager) gameMainLoop() {
localEventCost /= 1e6 defer func() {
commandCost /= 1e6 if err := recover(); err != nil {
logger.Info("[GAME MAIN LOOP] cpu time cost detail, routeCost: %vms, tickCost: %vms, localEventCost: %vms, commandCost: %vms", logger.Error("!!! GAME MAIN LOOP PANIC !!!")
routeCost, tickCost, localEventCost, commandCost) logger.Error("error: %v", err)
totalCost := routeCost + tickCost + localEventCost + commandCost logger.Error("stack: %v", logger.Stack())
logger.Info("[GAME MAIN LOOP] cpu time cost percent, routeCost: %v%%, tickCost: %v%%, localEventCost: %v%%, commandCost: %v%%",
float32(routeCost)/float32(totalCost)*100.0,
float32(tickCost)/float32(totalCost)*100.0,
float32(localEventCost)/float32(totalCost)*100.0,
float32(commandCost)/float32(totalCost)*100.0)
logger.Info("[GAME MAIN LOOP] total cpu time cost detail, totalCost: %vms",
totalCost)
logger.Info("[GAME MAIN LOOP] total cpu time cost percent, totalCost: %v%%",
float32(totalCost)/float32(intervalTime/1e6)*100.0)
lastTime = now
routeCost = 0
tickCost = 0
localEventCost = 0
commandCost = 0
}
select {
case netMsg := <-g.messageQueue.GetNetMsg():
// 接收客户端消息
start := time.Now().UnixNano()
ROUTE_MANAGER.RouteHandle(netMsg)
end := time.Now().UnixNano()
routeCost += end - start
case <-TICK_MANAGER.ticker.C:
// 游戏服务器定时帧
start := time.Now().UnixNano()
TICK_MANAGER.OnGameServerTick()
end := time.Now().UnixNano()
tickCost += end - start
case localEvent := <-LOCAL_EVENT_MANAGER.localEventChan:
// 处理本地事件
start := time.Now().UnixNano()
LOCAL_EVENT_MANAGER.LocalEventHandle(localEvent)
end := time.Now().UnixNano()
localEventCost += end - start
case command := <-COMMAND_MANAGER.commandTextInput:
// 处理传入的命令 (普通玩家 GM命令)
start := time.Now().UnixNano()
COMMAND_MANAGER.HandleCommand(command)
end := time.Now().UnixNano()
commandCost += end - start
}
} }
}() }()
intervalTime := time.Second.Nanoseconds() * 60
lastTime := time.Now().UnixNano()
routeCost := int64(0)
tickCost := int64(0)
localEventCost := int64(0)
commandCost := int64(0)
for {
now := time.Now().UnixNano()
if now-lastTime > intervalTime {
routeCost /= 1e6
tickCost /= 1e6
localEventCost /= 1e6
commandCost /= 1e6
logger.Info("[GAME MAIN LOOP] cpu time cost detail, routeCost: %vms, tickCost: %vms, localEventCost: %vms, commandCost: %vms",
routeCost, tickCost, localEventCost, commandCost)
totalCost := routeCost + tickCost + localEventCost + commandCost
logger.Info("[GAME MAIN LOOP] cpu time cost percent, routeCost: %v%%, tickCost: %v%%, localEventCost: %v%%, commandCost: %v%%",
float32(routeCost)/float32(totalCost)*100.0,
float32(tickCost)/float32(totalCost)*100.0,
float32(localEventCost)/float32(totalCost)*100.0,
float32(commandCost)/float32(totalCost)*100.0)
logger.Info("[GAME MAIN LOOP] total cpu time cost detail, totalCost: %vms",
totalCost)
logger.Info("[GAME MAIN LOOP] total cpu time cost percent, totalCost: %v%%",
float32(totalCost)/float32(intervalTime/1e6)*100.0)
lastTime = now
routeCost = 0
tickCost = 0
localEventCost = 0
commandCost = 0
}
select {
case netMsg := <-g.messageQueue.GetNetMsg():
// 接收客户端消息
start := time.Now().UnixNano()
ROUTE_MANAGER.RouteHandle(netMsg)
end := time.Now().UnixNano()
routeCost += end - start
case <-TICK_MANAGER.ticker.C:
// 游戏服务器定时帧
start := time.Now().UnixNano()
TICK_MANAGER.OnGameServerTick()
end := time.Now().UnixNano()
tickCost += end - start
case localEvent := <-LOCAL_EVENT_MANAGER.localEventChan:
// 处理本地事件
start := time.Now().UnixNano()
LOCAL_EVENT_MANAGER.LocalEventHandle(localEvent)
end := time.Now().UnixNano()
localEventCost += end - start
case command := <-COMMAND_MANAGER.commandTextInput:
// 处理传入的命令 (普通玩家 GM命令)
start := time.Now().UnixNano()
COMMAND_MANAGER.HandleCommand(command)
end := time.Now().UnixNano()
commandCost += end - start
}
}
} }
func (g *GameManager) Stop() { func (g *GameManager) Stop() {
// 下线玩家
userList := USER_MANAGER.GetAllOnlineUserList()
for _, player := range userList {
g.DisconnectPlayer(player.PlayerID, kcp.EnetServerShutdown)
}
time.Sleep(time.Second * 5)
// 保存玩家数据 // 保存玩家数据
LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{ LOCAL_EVENT_MANAGER.localEventChan <- &LocalEvent{
EventId: RunUserCopyAndSave, EventId: RunUserCopyAndSave,
} }
time.Sleep(time.Second * 3) time.Sleep(time.Second * 5)
// g.worldManager.worldStatic.SaveTerrain() // g.worldManager.worldStatic.SaveTerrain()
} }
@@ -197,6 +222,14 @@ func (g *GameManager) ReconnectPlayer(userId uint32) {
g.SendMsg(cmd.ClientReconnectNotify, userId, 0, new(proto.ClientReconnectNotify)) g.SendMsg(cmd.ClientReconnectNotify, userId, 0, new(proto.ClientReconnectNotify))
} }
func (g *GameManager) DisconnectPlayer(userId uint32) { func (g *GameManager) DisconnectPlayer(userId uint32, reason uint32) {
g.SendMsg(cmd.ServerDisconnectClientNotify, userId, 0, new(proto.ServerDisconnectClientNotify)) g.messageQueue.SendToGate("1", &mq.NetMsg{
MsgType: mq.MsgTypeConnCtrl,
EventId: mq.KickPlayerNotify,
ConnCtrlMsg: &mq.ConnCtrlMsg{
KickUserId: userId,
KickReason: reason,
},
})
// g.SendMsg(cmd.ServerDisconnectClientNotify, userId, 0, new(proto.ServerDisconnectClientNotify))
} }

View File

@@ -2,6 +2,7 @@ package game
import ( import (
"hk4e/common/mq" "hk4e/common/mq"
"hk4e/gate/kcp"
"hk4e/gs/model" "hk4e/gs/model"
"hk4e/pkg/logger" "hk4e/pkg/logger"
"hk4e/protocol/cmd" "hk4e/protocol/cmd"
@@ -37,8 +38,7 @@ func (r *RouteManager) doRoute(cmdId uint16, userId uint32, clientSeq uint32, pa
player := USER_MANAGER.GetOnlineUser(userId) player := USER_MANAGER.GetOnlineUser(userId)
if player == nil { if player == nil {
logger.Error("player is nil, uid: %v", userId) logger.Error("player is nil, uid: %v", userId)
// 临时为了调试便捷搞的重连 生产环境请务必去除 不然新用户会一直重连不能进入 GAME_MANAGER.DisconnectPlayer(userId, kcp.EnetNotFoundSession)
// GAME_MANAGER.ReconnectPlayer(userId)
return return
} }
player.ClientSeq = clientSeq player.ClientSeq = clientSeq
@@ -119,22 +119,30 @@ func (r *RouteManager) InitRoute() {
} }
func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) { func (r *RouteManager) RouteHandle(netMsg *mq.NetMsg) {
if netMsg.MsgType != mq.MsgTypeGame { switch netMsg.MsgType {
return case mq.MsgTypeGame:
} gameMsg := netMsg.GameMsg
gameMsg := netMsg.GameMsg switch netMsg.EventId {
switch netMsg.EventId { case mq.NormalMsg:
case mq.NormalMsg: if gameMsg.CmdId == cmd.PlayerLoginReq {
r.doRoute(gameMsg.CmdId, gameMsg.UserId, gameMsg.ClientSeq, gameMsg.PayloadMessage) GAME_MANAGER.PlayerLoginReq(gameMsg.UserId, gameMsg.ClientSeq, gameMsg.PayloadMessage)
case mq.UserRegNotify: return
GAME_MANAGER.OnReg(gameMsg.UserId, gameMsg.ClientSeq, gameMsg.PayloadMessage) }
case mq.UserLoginNotify: if gameMsg.CmdId == cmd.SetPlayerBornDataReq {
GAME_MANAGER.OnLogin(gameMsg.UserId, gameMsg.ClientSeq) GAME_MANAGER.SetPlayerBornDataReq(gameMsg.UserId, gameMsg.ClientSeq, gameMsg.PayloadMessage)
case mq.UserOfflineNotify: return
GAME_MANAGER.OnUserOffline(gameMsg.UserId) }
case mq.ClientRttNotify: r.doRoute(gameMsg.CmdId, gameMsg.UserId, gameMsg.ClientSeq, gameMsg.PayloadMessage)
GAME_MANAGER.ClientRttNotify(gameMsg.UserId, gameMsg.ClientRtt) case mq.UserOfflineNotify:
case mq.ClientTimeNotify: GAME_MANAGER.OnUserOffline(gameMsg.UserId)
GAME_MANAGER.ClientTimeNotify(gameMsg.UserId, gameMsg.ClientTime) }
case mq.MsgTypeConnCtrl:
connCtrlMsg := netMsg.ConnCtrlMsg
switch netMsg.EventId {
case mq.ClientRttNotify:
GAME_MANAGER.ClientRttNotify(connCtrlMsg.UserId, connCtrlMsg.ClientRtt)
case mq.ClientTimeNotify:
GAME_MANAGER.ClientTimeNotify(connCtrlMsg.UserId, connCtrlMsg.ClientTime)
}
} }
} }

View File

@@ -18,6 +18,9 @@ func DoForward[IET model.InvokeEntryType](player *model.Player, req pb.Message,
} }
cmdId := cmdProtoMap.GetCmdIdByProtoObj(req) cmdId := cmdProtoMap.GetCmdIdByProtoObj(req)
world := WORLD_MANAGER.GetWorldByID(player.WorldId) world := WORLD_MANAGER.GetWorldByID(player.WorldId)
if world == nil {
return
}
if invokeHandler.AllLen() == 0 && invokeHandler.AllExceptCurLen() == 0 && invokeHandler.HostLen() == 0 { if invokeHandler.AllLen() == 0 && invokeHandler.AllExceptCurLen() == 0 && invokeHandler.HostLen() == 0 {
ntf := cmdProtoMap.GetProtoObjByCmdId(cmdId) ntf := cmdProtoMap.GetProtoObjByCmdId(cmdId)
for _, fieldName := range copyFieldList { for _, fieldName := range copyFieldList {
@@ -90,6 +93,9 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
return return
} }
world := WORLD_MANAGER.GetWorldByID(player.WorldId) world := WORLD_MANAGER.GetWorldByID(player.WorldId)
if world == nil {
return
}
scene := world.GetSceneById(player.SceneId) scene := world.GetSceneById(player.SceneId)
for _, entry := range req.InvokeList { for _, entry := range req.InvokeList {
switch entry.ArgumentType { switch entry.ArgumentType {

View File

@@ -14,6 +14,20 @@ import (
pb "google.golang.org/protobuf/proto" pb "google.golang.org/protobuf/proto"
) )
func (g *GameManager) PlayerLoginReq(userId uint32, clientSeq uint32, payloadMsg pb.Message) {
logger.Info("user login req, uid: %v", userId)
req := payloadMsg.(*proto.PlayerLoginReq)
logger.Debug("login data: %v", req)
g.OnLogin(userId, clientSeq)
}
func (g *GameManager) SetPlayerBornDataReq(userId uint32, clientSeq uint32, payloadMsg pb.Message) {
logger.Info("user reg req, uid: %v", userId)
req := payloadMsg.(*proto.SetPlayerBornDataReq)
logger.Debug("reg data: %v", req)
g.OnReg(userId, clientSeq, req)
}
func (g *GameManager) OnLogin(userId uint32, clientSeq uint32) { func (g *GameManager) OnLogin(userId uint32, clientSeq uint32) {
logger.Info("user login, uid: %v", userId) logger.Info("user login, uid: %v", userId)
player, asyncWait := USER_MANAGER.OnlineUser(userId, clientSeq) player, asyncWait := USER_MANAGER.OnlineUser(userId, clientSeq)
@@ -117,6 +131,17 @@ func (g *GameManager) LoginNotify(userId uint32, player *model.Player, clientSeq
g.SendMsg(cmd.PlayerStoreNotify, userId, clientSeq, g.PacketPlayerStoreNotify(player)) g.SendMsg(cmd.PlayerStoreNotify, userId, clientSeq, g.PacketPlayerStoreNotify(player))
g.SendMsg(cmd.AvatarDataNotify, userId, clientSeq, g.PacketAvatarDataNotify(player)) g.SendMsg(cmd.AvatarDataNotify, userId, clientSeq, g.PacketAvatarDataNotify(player))
g.SendMsg(cmd.OpenStateUpdateNotify, userId, clientSeq, g.PacketOpenStateUpdateNotify()) g.SendMsg(cmd.OpenStateUpdateNotify, userId, clientSeq, g.PacketOpenStateUpdateNotify())
playerLoginRsp := &proto.PlayerLoginRsp{
IsUseAbilityHash: true,
AbilityHashCode: -228935105,
GameBiz: "hk4e_cn",
IsScOpen: false,
RegisterCps: "taptap",
CountryCode: "CN",
Birthday: "2000-01-01",
TotalTickTime: 1185941.871788,
}
g.SendMsg(cmd.PlayerLoginRsp, userId, clientSeq, playerLoginRsp)
} }
func (g *GameManager) PacketPlayerDataNotify(player *model.Player) *proto.PlayerDataNotify { func (g *GameManager) PacketPlayerDataNotify(player *model.Player) *proto.PlayerDataNotify {

View File

@@ -140,7 +140,7 @@ func (g *GameManager) JoinPlayerSceneReq(player *model.Player, payloadMsg pb.Mes
g.SendMsg(cmd.LeaveWorldNotify, player.PlayerID, player.ClientSeq, new(proto.LeaveWorldNotify)) g.SendMsg(cmd.LeaveWorldNotify, player.PlayerID, player.ClientSeq, new(proto.LeaveWorldNotify))
// g.LoginNotify(player.PlayerID, player, 0) g.LoginNotify(player.PlayerID, player, 0)
if hostPlayer.SceneLoadState == model.SceneEnterDone { if hostPlayer.SceneLoadState == model.SceneEnterDone {
delete(hostWorld.waitEnterPlayerMap, player.PlayerID) delete(hostWorld.waitEnterPlayerMap, player.PlayerID)
@@ -268,7 +268,6 @@ func (g *GameManager) UserLeaveWorld(player *model.Player) bool {
return false return false
} }
} }
g.UserWorldRemovePlayer(oldWorld, player)
g.ReconnectPlayer(player.PlayerID) g.ReconnectPlayer(player.PlayerID)
return true return true
} }
@@ -318,12 +317,13 @@ func (g *GameManager) UserWorldRemovePlayer(world *World, player *model.Player)
world.RemovePlayer(player) world.RemovePlayer(player)
player.WorldId = 0 player.WorldId = 0
if world.multiplayer && world.GetWorldPlayerNum() > 0 {
g.UpdateWorldPlayerInfo(world, player)
}
if world.owner.PlayerID == player.PlayerID { if world.owner.PlayerID == player.PlayerID {
// 房主离开销毁世界 // 房主离开销毁世界
WORLD_MANAGER.DestroyWorld(world.id) WORLD_MANAGER.DestroyWorld(world.id)
return
}
if world.multiplayer && world.GetWorldPlayerNum() > 0 {
g.UpdateWorldPlayerInfo(world, player)
} }
} }

View File

@@ -1,10 +1,11 @@
package game package game
import ( import (
gdc "hk4e/gs/config"
"strings" "strings"
"time" "time"
gdc "hk4e/gs/config"
"hk4e/gdconf" "hk4e/gdconf"
"hk4e/gs/constant" "hk4e/gs/constant"
"hk4e/gs/model" "hk4e/gs/model"
@@ -529,7 +530,7 @@ func (g *GameManager) SetPlayerStamina(player *model.Player, stamina uint32) {
// 设置玩家的耐力 // 设置玩家的耐力
prop := constant.PlayerPropertyConst.PROP_CUR_PERSIST_STAMINA prop := constant.PlayerPropertyConst.PROP_CUR_PERSIST_STAMINA
player.PropertiesMap[prop] = stamina player.PropertiesMap[prop] = stamina
//logger.Debug("player stamina set, stamina: %v", stamina) // logger.Debug("player stamina set, stamina: %v", stamina)
// PacketPlayerPropNotify // PacketPlayerPropNotify
g.PlayerPropNotify(player, prop) g.PlayerPropNotify(player, prop)

View File

@@ -1,10 +1,11 @@
package game package game
import ( import (
"hk4e/protocol/cmd"
"math" "math"
"time" "time"
"hk4e/protocol/cmd"
"hk4e/common/mq" "hk4e/common/mq"
"hk4e/gs/constant" "hk4e/gs/constant"
"hk4e/gs/game/aoi" "hk4e/gs/game/aoi"
@@ -64,7 +65,7 @@ func (w *WorldManager) CreateWorld(owner *model.Player) *World {
playerFirstEnterMap: make(map[uint32]int64), playerFirstEnterMap: make(map[uint32]int64),
waitEnterPlayerMap: make(map[uint32]int64), waitEnterPlayerMap: make(map[uint32]int64),
multiplayerTeam: CreateMultiplayerTeam(), multiplayerTeam: CreateMultiplayerTeam(),
peerMap: make(map[uint32]*model.Player), peerList: make([]*model.Player, 0),
} }
if world.IsBigWorld() { if world.IsBigWorld() {
world.aoiManager = aoi.NewAoiManager( world.aoiManager = aoi.NewAoiManager(
@@ -132,7 +133,7 @@ type World struct {
playerFirstEnterMap map[uint32]int64 // 玩家第一次进入世界的时间 key:uid value:进入时间 playerFirstEnterMap map[uint32]int64 // 玩家第一次进入世界的时间 key:uid value:进入时间
waitEnterPlayerMap map[uint32]int64 // 等待进入世界的列表 key:uid value:开始时间 waitEnterPlayerMap map[uint32]int64 // 等待进入世界的列表 key:uid value:开始时间
multiplayerTeam *MultiplayerTeam multiplayerTeam *MultiplayerTeam
peerMap map[uint32]*model.Player // key:玩家编号 value:player对象 peerList []*model.Player // 玩家编号列表
} }
func (w *World) GetNextWorldEntityId(entityType uint16) uint32 { func (w *World) GetNextWorldEntityId(entityType uint16) uint32 {
@@ -157,17 +158,23 @@ func (w *World) GetNextWorldEntityId(entityType uint16) uint32 {
// GetPlayerPeerId 获取当前玩家世界内编号 // GetPlayerPeerId 获取当前玩家世界内编号
func (w *World) GetPlayerPeerId(player *model.Player) uint32 { func (w *World) GetPlayerPeerId(player *model.Player) uint32 {
for peerId, worldPlayer := range w.peerMap { peerId := uint32(0)
for peerIdIndex, worldPlayer := range w.peerList {
if worldPlayer.PlayerID == player.PlayerID { if worldPlayer.PlayerID == player.PlayerID {
return peerId peerId = uint32(peerIdIndex) + 1
} }
} }
return 0 logger.Debug("get player peer id is: %v, uid: %v", peerId, player.PlayerID)
return peerId
} }
// GetNextPeerId 获取下一个世界内玩家编号 // GetPlayerByPeerId 通过世界内编号获取玩家
func (w *World) GetNextPeerId() uint32 { func (w *World) GetPlayerByPeerId(peerId uint32) *model.Player {
return uint32(len(w.playerMap) + 1) peerIdIndex := int(peerId) - 1
if peerIdIndex >= len(w.peerList) {
return nil
}
return w.peerList[peerIdIndex]
} }
// GetWorldPlayerNum 获取世界中玩家的数量 // GetWorldPlayerNum 获取世界中玩家的数量
@@ -176,7 +183,7 @@ func (w *World) GetWorldPlayerNum() int {
} }
func (w *World) AddPlayer(player *model.Player, sceneId uint32) { func (w *World) AddPlayer(player *model.Player, sceneId uint32) {
w.peerMap[w.GetNextPeerId()] = player w.peerList = append(w.peerList, player)
w.playerMap[player.PlayerID] = player w.playerMap[player.PlayerID] = player
// 将玩家自身当前的队伍角色信息复制到世界的玩家本地队伍 // 将玩家自身当前的队伍角色信息复制到世界的玩家本地队伍
team := player.TeamConfig.GetActiveTeam() team := player.TeamConfig.GetActiveTeam()
@@ -203,7 +210,8 @@ func (w *World) AddPlayer(player *model.Player, sceneId uint32) {
} }
func (w *World) RemovePlayer(player *model.Player) { func (w *World) RemovePlayer(player *model.Player) {
delete(w.peerMap, w.GetPlayerPeerId(player)) peerId := w.GetPlayerPeerId(player)
w.peerList = append(w.peerList[:peerId-1], w.peerList[peerId:]...)
scene := w.sceneMap[player.SceneId] scene := w.sceneMap[player.SceneId]
scene.RemovePlayer(player) scene.RemovePlayer(player)
delete(w.playerMap, player.PlayerID) delete(w.playerMap, player.PlayerID)
@@ -416,7 +424,7 @@ func (w *World) SetPlayerLocalTeam(player *model.Player, avatarIdList []uint32)
} }
func (w *World) copyLocalTeamToWorld(start int, end int, peerId uint32) { func (w *World) copyLocalTeamToWorld(start int, end int, peerId uint32) {
player := w.peerMap[peerId] player := w.GetPlayerByPeerId(peerId)
localTeam := w.GetPlayerLocalTeam(player) localTeam := w.GetPlayerLocalTeam(player)
localTeamIndex := 0 localTeamIndex := 0
for index := start; index <= end; index++ { for index := start; index <= end; index++ {
@@ -438,6 +446,10 @@ func (w *World) copyLocalTeamToWorld(start int, end int, peerId uint32) {
// UpdateMultiplayerTeam 整合所有玩家的本地队伍计算出世界队伍 // UpdateMultiplayerTeam 整合所有玩家的本地队伍计算出世界队伍
func (w *World) UpdateMultiplayerTeam() { func (w *World) UpdateMultiplayerTeam() {
_, exist := w.playerMap[w.owner.PlayerID]
if !exist {
return
}
w.multiplayerTeam.worldTeam = make([]*WorldAvatar, 4) w.multiplayerTeam.worldTeam = make([]*WorldAvatar, 4)
switch w.GetWorldPlayerNum() { switch w.GetWorldPlayerNum() {
case 1: case 1: