mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-11 14:32:26 +08:00
重构网关服务器
This commit is contained in:
361
gate/net/forward.go
Normal file
361
gate/net/forward.go
Normal file
@@ -0,0 +1,361 @@
|
||||
package net
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hk4e/dispatch/controller"
|
||||
"hk4e/gate/kcp"
|
||||
"hk4e/pkg/endec"
|
||||
"hk4e/pkg/httpclient"
|
||||
"hk4e/pkg/logger"
|
||||
"hk4e/pkg/random"
|
||||
"hk4e/protocol/cmd"
|
||||
"hk4e/protocol/proto"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
ConnWaitToken = iota
|
||||
ConnWaitLogin
|
||||
ConnAlive
|
||||
ConnClose
|
||||
)
|
||||
|
||||
// 发送消息到GS
|
||||
func (k *KcpConnectManager) recvMsgHandle(protoMsg *ProtoMsg, session *Session) {
|
||||
userId := session.userId
|
||||
headMeta := session.headMeta
|
||||
connState := session.connState
|
||||
if protoMsg.HeadMessage == nil {
|
||||
logger.LOG.Error("recv null head msg: %v", protoMsg)
|
||||
}
|
||||
headMeta.seq = protoMsg.HeadMessage.ClientSequenceId
|
||||
// gate本地处理的请求
|
||||
switch protoMsg.CmdId {
|
||||
case cmd.GetPlayerTokenReq:
|
||||
// 获取玩家token请求
|
||||
if connState != ConnWaitToken {
|
||||
return
|
||||
}
|
||||
getPlayerTokenReq := protoMsg.PayloadMessage.(*proto.GetPlayerTokenReq)
|
||||
getPlayerTokenRsp := k.getPlayerToken(getPlayerTokenReq, session)
|
||||
if getPlayerTokenRsp == nil {
|
||||
return
|
||||
}
|
||||
// 返回数据到客户端
|
||||
rsp := new(ProtoMsg)
|
||||
rsp.ConvId = protoMsg.ConvId
|
||||
rsp.CmdId = cmd.GetPlayerTokenRsp
|
||||
rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
|
||||
rsp.PayloadMessage = getPlayerTokenRsp
|
||||
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初始化相关数据
|
||||
netMsg := new(cmd.NetMsg)
|
||||
netMsg.UserId = userId
|
||||
netMsg.EventId = cmd.UserLoginNotify
|
||||
netMsg.ClientSeq = headMeta.seq
|
||||
k.netMsgInput <- netMsg
|
||||
logger.LOG.Info("send to gs user login ok, ConvId: %v, UserId: %v", protoMsg.ConvId, netMsg.UserId)
|
||||
case cmd.SetPlayerBornDataReq:
|
||||
// 玩家注册请求
|
||||
if connState != ConnAlive {
|
||||
return
|
||||
}
|
||||
netMsg := new(cmd.NetMsg)
|
||||
netMsg.UserId = userId
|
||||
netMsg.EventId = cmd.UserRegNotify
|
||||
netMsg.CmdId = cmd.SetPlayerBornDataReq
|
||||
netMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId
|
||||
netMsg.PayloadMessage = protoMsg.PayloadMessage
|
||||
k.netMsgInput <- netMsg
|
||||
case cmd.PlayerForceExitReq:
|
||||
// 玩家退出游戏请求
|
||||
if connState != ConnAlive {
|
||||
return
|
||||
}
|
||||
k.kcpEventInput <- &KcpEvent{
|
||||
ConvId: protoMsg.ConvId,
|
||||
EventId: KcpConnForceClose,
|
||||
EventMessage: uint32(kcp.EnetClientClose),
|
||||
}
|
||||
case cmd.PingReq:
|
||||
// ping请求
|
||||
if connState != ConnAlive {
|
||||
return
|
||||
}
|
||||
pingReq := protoMsg.PayloadMessage.(*proto.PingReq)
|
||||
logger.LOG.Debug("user ping req, data: %v", pingReq.String())
|
||||
// 返回数据到客户端
|
||||
// TODO 记录客户端最后一次ping时间做超时下线处理
|
||||
pingRsp := new(proto.PingRsp)
|
||||
pingRsp.ClientTime = pingReq.ClientTime
|
||||
rsp := new(ProtoMsg)
|
||||
rsp.ConvId = protoMsg.ConvId
|
||||
rsp.CmdId = cmd.PingRsp
|
||||
rsp.HeadMessage = k.getHeadMsg(protoMsg.HeadMessage.ClientSequenceId)
|
||||
rsp.PayloadMessage = pingRsp
|
||||
k.localMsgOutput <- rsp
|
||||
// 通知GS玩家客户端的本地时钟
|
||||
netMsg := new(cmd.NetMsg)
|
||||
netMsg.UserId = userId
|
||||
netMsg.EventId = cmd.ClientTimeNotify
|
||||
netMsg.ClientTime = pingReq.ClientTime
|
||||
k.netMsgInput <- netMsg
|
||||
// RTT
|
||||
logger.LOG.Debug("convId: %v, RTO: %v, SRTT: %v, RTTVar: %v", protoMsg.ConvId, session.conn.GetRTO(), session.conn.GetSRTT(), session.conn.GetSRTTVar())
|
||||
// 客户端往返时延通知
|
||||
rtt := session.conn.GetSRTT()
|
||||
// 通知GS玩家客户端往返时延
|
||||
netMsg = new(cmd.NetMsg)
|
||||
netMsg.UserId = userId
|
||||
netMsg.EventId = cmd.ClientRttNotify
|
||||
netMsg.ClientRtt = uint32(rtt)
|
||||
k.netMsgInput <- netMsg
|
||||
default:
|
||||
// 转发到GS
|
||||
// 未登录禁止访问GS
|
||||
if connState != ConnAlive {
|
||||
return
|
||||
}
|
||||
netMsg := new(cmd.NetMsg)
|
||||
netMsg.UserId = userId
|
||||
netMsg.EventId = cmd.NormalMsg
|
||||
netMsg.CmdId = protoMsg.CmdId
|
||||
netMsg.ClientSeq = protoMsg.HeadMessage.ClientSequenceId
|
||||
netMsg.PayloadMessage = protoMsg.PayloadMessage
|
||||
k.netMsgInput <- netMsg
|
||||
}
|
||||
}
|
||||
|
||||
// 从GS接收消息
|
||||
func (k *KcpConnectManager) sendMsgHandle() {
|
||||
logger.LOG.Debug("send msg handle start")
|
||||
kcpRawSendChanMap := make(map[uint64]chan *ProtoMsg)
|
||||
userIdConvMap := make(map[uint32]uint64)
|
||||
sendToClientFn := func(protoMsg *ProtoMsg) {
|
||||
// 分发到每个连接具体的发送协程
|
||||
kcpRawSendChan := kcpRawSendChanMap[protoMsg.ConvId]
|
||||
if kcpRawSendChan != nil {
|
||||
select {
|
||||
case kcpRawSendChan <- protoMsg:
|
||||
default:
|
||||
logger.LOG.Error("kcpRawSendChan is full, convId: %v", protoMsg.ConvId)
|
||||
}
|
||||
} else {
|
||||
logger.LOG.Error("kcpRawSendChan is nil, convId: %v", protoMsg.ConvId)
|
||||
}
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case session := <-k.createSessionChan:
|
||||
kcpRawSendChanMap[session.conn.GetConv()] = session.kcpRawSendChan
|
||||
userIdConvMap[session.userId] = session.conn.GetConv()
|
||||
case session := <-k.destroySessionChan:
|
||||
delete(kcpRawSendChanMap, session.conn.GetConv())
|
||||
delete(userIdConvMap, session.userId)
|
||||
close(session.kcpRawSendChan)
|
||||
case protoMsg := <-k.localMsgOutput:
|
||||
sendToClientFn(protoMsg)
|
||||
case netMsg := <-k.netMsgOutput:
|
||||
convId, exist := userIdConvMap[netMsg.UserId]
|
||||
if !exist {
|
||||
logger.LOG.Error("can not find convId by userId")
|
||||
continue
|
||||
}
|
||||
if netMsg.EventId == cmd.NormalMsg {
|
||||
protoMsg := new(ProtoMsg)
|
||||
protoMsg.ConvId = convId
|
||||
protoMsg.CmdId = netMsg.CmdId
|
||||
protoMsg.HeadMessage = k.getHeadMsg(netMsg.ClientSeq)
|
||||
protoMsg.PayloadMessage = netMsg.PayloadMessage
|
||||
sendToClientFn(protoMsg)
|
||||
} else {
|
||||
logger.LOG.Error("recv unknown event from game server, event id: %v", netMsg.EventId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (k *KcpConnectManager) getHeadMsg(clientSeq uint32) (headMsg *proto.PacketHead) {
|
||||
headMsg = new(proto.PacketHead)
|
||||
if clientSeq != 0 {
|
||||
headMsg.ClientSequenceId = clientSeq
|
||||
headMsg.SentMs = uint64(time.Now().UnixMilli())
|
||||
}
|
||||
return headMsg
|
||||
}
|
||||
|
||||
func (k *KcpConnectManager) getPlayerToken(req *proto.GetPlayerTokenReq, session *Session) (rsp *proto.GetPlayerTokenRsp) {
|
||||
tokenVerifyRsp, err := httpclient.Post[controller.TokenVerifyRsp]("http://127.0.0.1:8080/gate/token/verify", &controller.TokenVerifyReq{
|
||||
AccountId: req.AccountUid,
|
||||
AccountToken: req.AccountToken,
|
||||
}, "")
|
||||
if err != nil {
|
||||
logger.LOG.Error("verify token error: %v", err)
|
||||
return nil
|
||||
}
|
||||
if !tokenVerifyRsp.Valid {
|
||||
logger.LOG.Error("token error")
|
||||
return nil
|
||||
}
|
||||
// comboToken验证成功
|
||||
if tokenVerifyRsp.Forbid {
|
||||
// 封号通知
|
||||
rsp = new(proto.GetPlayerTokenRsp)
|
||||
rsp.Uid = tokenVerifyRsp.PlayerID
|
||||
rsp.IsProficientPlayer = true
|
||||
rsp.Retcode = 21
|
||||
rsp.Msg = "FORBID_CHEATING_PLUGINS"
|
||||
rsp.BlackUidEndTime = tokenVerifyRsp.ForbidEndTime
|
||||
if rsp.BlackUidEndTime == 0 {
|
||||
rsp.BlackUidEndTime = 2051193600 // 2035-01-01 00:00:00
|
||||
}
|
||||
rsp.RegPlatform = 3
|
||||
rsp.CountryCode = "US"
|
||||
addr := session.conn.RemoteAddr().String()
|
||||
split := strings.Split(addr, ":")
|
||||
rsp.ClientIpStr = split[0]
|
||||
return rsp
|
||||
}
|
||||
oldSession := k.GetSessionByUserId(tokenVerifyRsp.PlayerID)
|
||||
if oldSession != nil {
|
||||
// 顶号
|
||||
k.kcpEventInput <- &KcpEvent{
|
||||
ConvId: oldSession.conn.GetConv(),
|
||||
EventId: KcpConnForceClose,
|
||||
EventMessage: uint32(kcp.EnetServerRelogin),
|
||||
}
|
||||
}
|
||||
// 关联玩家uid和连接信息
|
||||
session.userId = tokenVerifyRsp.PlayerID
|
||||
session.connState = ConnWaitLogin
|
||||
k.SetSession(session, session.conn.GetConv(), session.userId)
|
||||
k.createSessionChan <- session
|
||||
// 返回响应
|
||||
rsp = new(proto.GetPlayerTokenRsp)
|
||||
rsp.Uid = tokenVerifyRsp.PlayerID
|
||||
rsp.AccountUid = req.AccountUid
|
||||
rsp.Token = req.AccountToken
|
||||
data := make([]byte, 16+32)
|
||||
rand.Read(data)
|
||||
rsp.SecurityCmdBuffer = data[16:]
|
||||
rsp.ClientVersionRandomKey = fmt.Sprintf("%03x-%012x", data[:3], data[4:16])
|
||||
rsp.AccountType = 1
|
||||
rsp.IsProficientPlayer = true
|
||||
rsp.PlatformType = 3
|
||||
rsp.ChannelId = 1
|
||||
rsp.SubChannelId = 1
|
||||
rsp.RegPlatform = 2
|
||||
rsp.Birthday = "2000-01-01"
|
||||
addr := session.conn.RemoteAddr().String()
|
||||
split := strings.Split(addr, ":")
|
||||
rsp.ClientIpStr = split[0]
|
||||
if req.GetKeyId() != 0 {
|
||||
logger.LOG.Debug("do hk4e 2.8 rsa logic")
|
||||
keyId := strconv.Itoa(int(req.GetKeyId()))
|
||||
encPubPrivKey, exist := k.encRsaKeyMap[keyId]
|
||||
if !exist {
|
||||
logger.LOG.Error("can not found key id: %v", keyId)
|
||||
return
|
||||
}
|
||||
pubKey, err := endec.RsaParsePubKeyByPrivKey(encPubPrivKey)
|
||||
if err != nil {
|
||||
logger.LOG.Error("parse rsa pub key error: %v", err)
|
||||
return nil
|
||||
}
|
||||
signPrivkey, err := endec.RsaParsePrivKey(k.signRsaKey)
|
||||
if err != nil {
|
||||
logger.LOG.Error("parse rsa priv key error: %v", err)
|
||||
return nil
|
||||
}
|
||||
clientSeedBase64 := req.GetClientRandKey()
|
||||
clientSeedEnc, err := base64.StdEncoding.DecodeString(clientSeedBase64)
|
||||
if err != nil {
|
||||
logger.LOG.Error("parse client seed base64 error: %v", err)
|
||||
return nil
|
||||
}
|
||||
clientSeed, err := endec.RsaDecrypt(clientSeedEnc, signPrivkey)
|
||||
if err != nil {
|
||||
logger.LOG.Error("rsa dec error: %v", err)
|
||||
return rsp
|
||||
}
|
||||
clientSeedUint64 := uint64(0)
|
||||
err = binary.Read(bytes.NewReader(clientSeed), binary.BigEndian, &clientSeedUint64)
|
||||
if err != nil {
|
||||
logger.LOG.Error("parse client seed to uint64 error: %v", err)
|
||||
return rsp
|
||||
}
|
||||
timeRand := random.GetTimeRand()
|
||||
serverSeedUint64 := timeRand.Uint64()
|
||||
session.seed = serverSeedUint64
|
||||
session.changeXorKey = true
|
||||
seedUint64 := serverSeedUint64 ^ clientSeedUint64
|
||||
seedBuf := new(bytes.Buffer)
|
||||
err = binary.Write(seedBuf, binary.BigEndian, seedUint64)
|
||||
if err != nil {
|
||||
logger.LOG.Error("conv seed uint64 to bytes error: %v", err)
|
||||
return rsp
|
||||
}
|
||||
seed := seedBuf.Bytes()
|
||||
seedEnc, err := endec.RsaEncrypt(seed, pubKey)
|
||||
if err != nil {
|
||||
logger.LOG.Error("rsa enc error: %v", err)
|
||||
return rsp
|
||||
}
|
||||
seedSign, err := endec.RsaSign(seed, signPrivkey)
|
||||
if err != nil {
|
||||
logger.LOG.Error("rsa sign error: %v", err)
|
||||
return rsp
|
||||
}
|
||||
rsp.KeyId = req.KeyId
|
||||
rsp.ServerRandKey = base64.StdEncoding.EncodeToString(seedEnc)
|
||||
rsp.Sign = base64.StdEncoding.EncodeToString(seedSign)
|
||||
}
|
||||
return rsp
|
||||
}
|
||||
|
||||
func (k *KcpConnectManager) playerLogin(req *proto.PlayerLoginReq, session *Session) (rsp *proto.PlayerLoginRsp) {
|
||||
logger.LOG.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
|
||||
}
|
||||
Reference in New Issue
Block a user