mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-04 16:02:26 +08:00
优化登录、离线、服务器重启等会话连接问题
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user