mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-03-01 00:35:36 +08:00
优化机器人
This commit is contained in:
@@ -1,12 +0,0 @@
|
|||||||
package kcp
|
|
||||||
|
|
||||||
import "golang.org/x/net/ipv4"
|
|
||||||
|
|
||||||
const (
|
|
||||||
batchSize = 16
|
|
||||||
)
|
|
||||||
|
|
||||||
type batchConn interface {
|
|
||||||
WriteBatch(ms []ipv4.Message, flags int) (int, error)
|
|
||||||
ReadBatch(ms []ipv4.Message, flags int) (int, error)
|
|
||||||
}
|
|
||||||
134
gate/kcp/enet.go
Normal file
134
gate/kcp/enet.go
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
package kcp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 原神Enet连接控制协议
|
||||||
|
// MM MM MM MM | LL LL LL LL | HH HH HH HH | EE EE EE EE | MM MM MM MM
|
||||||
|
// MM为表示连接状态的幻数 在开头的4字节和结尾的4字节
|
||||||
|
// LL和HH分别为convId的低4字节和高4字节
|
||||||
|
// EE为Enet事件类型 4字节
|
||||||
|
|
||||||
|
// Enet协议上报结构体
|
||||||
|
type Enet struct {
|
||||||
|
Addr string
|
||||||
|
ConvId uint64
|
||||||
|
ConnType uint8
|
||||||
|
EnetType uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enet连接状态类型
|
||||||
|
const (
|
||||||
|
ConnEnetSyn = 1
|
||||||
|
ConnEnetEst = 2
|
||||||
|
ConnEnetFin = 3
|
||||||
|
ConnEnetAddrChange = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
// Enet连接状态类型幻数
|
||||||
|
var MagicEnetSynHead, _ = hex.DecodeString("000000ff")
|
||||||
|
var MagicEnetSynTail, _ = hex.DecodeString("ffffffff")
|
||||||
|
var MagicEnetEstHead, _ = hex.DecodeString("00000145")
|
||||||
|
var MagicEnetEstTail, _ = hex.DecodeString("14514545")
|
||||||
|
var MagicEnetFinHead, _ = hex.DecodeString("00000194")
|
||||||
|
var MagicEnetFinTail, _ = hex.DecodeString("19419494")
|
||||||
|
|
||||||
|
// Enet事件类型
|
||||||
|
const (
|
||||||
|
EnetTimeout = 0
|
||||||
|
EnetClientClose = 1
|
||||||
|
EnetClientRebindFail = 2
|
||||||
|
EnetClientShutdown = 3
|
||||||
|
EnetServerRelogin = 4
|
||||||
|
EnetServerKick = 5
|
||||||
|
EnetServerShutdown = 6
|
||||||
|
EnetNotFoundSession = 7
|
||||||
|
EnetLoginUnfinished = 8
|
||||||
|
EnetPacketFreqTooHigh = 9
|
||||||
|
EnetPingTimeout = 10
|
||||||
|
EnetTranferFailed = 11
|
||||||
|
EnetServerKillClient = 12
|
||||||
|
EnetCheckMoveSpeed = 13
|
||||||
|
EnetAccountPasswordChange = 14
|
||||||
|
EnetClientEditorConnectKey = 987654321
|
||||||
|
EnetClientConnectKey = 1234567890
|
||||||
|
)
|
||||||
|
|
||||||
|
func BuildEnet(connType uint8, enetType uint32, conv uint64) []byte {
|
||||||
|
data := make([]byte, 20)
|
||||||
|
if connType == ConnEnetSyn {
|
||||||
|
copy(data[0:4], MagicEnetSynHead)
|
||||||
|
copy(data[16:20], MagicEnetSynTail)
|
||||||
|
} else if connType == ConnEnetEst {
|
||||||
|
copy(data[0:4], MagicEnetEstHead)
|
||||||
|
copy(data[16:20], MagicEnetEstTail)
|
||||||
|
} else if connType == ConnEnetFin {
|
||||||
|
copy(data[0:4], MagicEnetFinHead)
|
||||||
|
copy(data[16:20], MagicEnetFinTail)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
// conv的高四个字节和低四个字节分开
|
||||||
|
// 例如 00 00 01 45 | LL LL LL LL | HH HH HH HH | 49 96 02 d2 | 14 51 45 45
|
||||||
|
data[4] = uint8(conv >> 24)
|
||||||
|
data[5] = uint8(conv >> 16)
|
||||||
|
data[6] = uint8(conv >> 8)
|
||||||
|
data[7] = uint8(conv >> 0)
|
||||||
|
data[8] = uint8(conv >> 56)
|
||||||
|
data[9] = uint8(conv >> 48)
|
||||||
|
data[10] = uint8(conv >> 40)
|
||||||
|
data[11] = uint8(conv >> 32)
|
||||||
|
// Enet
|
||||||
|
data[12] = uint8(enetType >> 24)
|
||||||
|
data[13] = uint8(enetType >> 16)
|
||||||
|
data[14] = uint8(enetType >> 8)
|
||||||
|
data[15] = uint8(enetType >> 0)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseEnet(data []byte) (connType uint8, enetType uint32, conv uint64, err error) {
|
||||||
|
// 提取convId
|
||||||
|
conv = uint64(0)
|
||||||
|
conv += uint64(data[4]) << 24
|
||||||
|
conv += uint64(data[5]) << 16
|
||||||
|
conv += uint64(data[6]) << 8
|
||||||
|
conv += uint64(data[7]) << 0
|
||||||
|
conv += uint64(data[8]) << 56
|
||||||
|
conv += uint64(data[9]) << 48
|
||||||
|
conv += uint64(data[10]) << 40
|
||||||
|
conv += uint64(data[11]) << 32
|
||||||
|
// 提取Enet协议头部和尾部幻数
|
||||||
|
udpPayloadEnetHead := data[:4]
|
||||||
|
udpPayloadEnetTail := data[len(data)-4:]
|
||||||
|
// 提取Enet协议类型
|
||||||
|
enetTypeData := data[12:16]
|
||||||
|
enetTypeDataBuffer := bytes.NewBuffer(enetTypeData)
|
||||||
|
enetType = uint32(0)
|
||||||
|
_ = binary.Read(enetTypeDataBuffer, binary.BigEndian, &enetType)
|
||||||
|
equalHead := bytes.Equal(udpPayloadEnetHead, MagicEnetSynHead)
|
||||||
|
equalTail := bytes.Equal(udpPayloadEnetTail, MagicEnetSynTail)
|
||||||
|
if equalHead && equalTail {
|
||||||
|
// 客户端前置握手获取conv
|
||||||
|
connType = ConnEnetSyn
|
||||||
|
return connType, enetType, conv, nil
|
||||||
|
}
|
||||||
|
equalHead = bytes.Equal(udpPayloadEnetHead, MagicEnetEstHead)
|
||||||
|
equalTail = bytes.Equal(udpPayloadEnetTail, MagicEnetEstTail)
|
||||||
|
if equalHead && equalTail {
|
||||||
|
// 连接建立
|
||||||
|
connType = ConnEnetEst
|
||||||
|
return connType, enetType, conv, nil
|
||||||
|
}
|
||||||
|
equalHead = bytes.Equal(udpPayloadEnetHead, MagicEnetFinHead)
|
||||||
|
equalTail = bytes.Equal(udpPayloadEnetTail, MagicEnetFinTail)
|
||||||
|
if equalHead && equalTail {
|
||||||
|
// 连接断开
|
||||||
|
connType = ConnEnetFin
|
||||||
|
return connType, enetType, conv, nil
|
||||||
|
}
|
||||||
|
return 0, 0, 0, errors.New("unknown conn type")
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ package kcp
|
|||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/hex"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
@@ -64,17 +63,12 @@ type (
|
|||||||
ownConn bool // true if we created conn internally, false if provided by caller
|
ownConn bool // true if we created conn internally, false if provided by caller
|
||||||
kcp *KCP // KCP ARQ protocol
|
kcp *KCP // KCP ARQ protocol
|
||||||
l *Listener // pointing to the Listener object if it's been accepted by a Listener
|
l *Listener // pointing to the Listener object if it's been accepted by a Listener
|
||||||
// block BlockCrypt // block encryption object
|
|
||||||
|
|
||||||
// kcp receiving is based on packets
|
// kcp receiving is based on packets
|
||||||
// recvbuf turns packets into stream
|
// recvbuf turns packets into stream
|
||||||
recvbuf []byte
|
recvbuf []byte
|
||||||
bufptr []byte
|
bufptr []byte
|
||||||
|
|
||||||
// // FEC codec
|
|
||||||
// fecDecoder *fecDecoder
|
|
||||||
// fecEncoder *fecEncoder
|
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
remote net.Addr // remote peer address
|
remote net.Addr // remote peer address
|
||||||
rd time.Time // read deadline
|
rd time.Time // read deadline
|
||||||
@@ -98,9 +92,6 @@ type (
|
|||||||
socketReadErrorOnce sync.Once
|
socketReadErrorOnce sync.Once
|
||||||
socketWriteErrorOnce sync.Once
|
socketWriteErrorOnce sync.Once
|
||||||
|
|
||||||
// // nonce generator
|
|
||||||
// nonce Entropy
|
|
||||||
|
|
||||||
// packets waiting to be sent on wire
|
// packets waiting to be sent on wire
|
||||||
txqueue []ipv4.Message
|
txqueue []ipv4.Message
|
||||||
xconn batchConn // for x/net
|
xconn batchConn // for x/net
|
||||||
@@ -126,8 +117,6 @@ type (
|
|||||||
func newUDPSession(conv uint64, l *Listener, conn net.PacketConn, ownConn bool, remote net.Addr) *UDPSession {
|
func newUDPSession(conv uint64, l *Listener, conn net.PacketConn, ownConn bool, remote net.Addr) *UDPSession {
|
||||||
sess := new(UDPSession)
|
sess := new(UDPSession)
|
||||||
sess.die = make(chan struct{})
|
sess.die = make(chan struct{})
|
||||||
// sess.nonce = new(nonceAES128)
|
|
||||||
// sess.nonce.Init()
|
|
||||||
sess.chReadEvent = make(chan struct{}, 1)
|
sess.chReadEvent = make(chan struct{}, 1)
|
||||||
sess.chWriteEvent = make(chan struct{}, 1)
|
sess.chWriteEvent = make(chan struct{}, 1)
|
||||||
sess.chSocketReadError = make(chan struct{})
|
sess.chSocketReadError = make(chan struct{})
|
||||||
@@ -136,7 +125,6 @@ func newUDPSession(conv uint64, l *Listener, conn net.PacketConn, ownConn bool,
|
|||||||
sess.conn = conn
|
sess.conn = conn
|
||||||
sess.ownConn = ownConn
|
sess.ownConn = ownConn
|
||||||
sess.l = l
|
sess.l = l
|
||||||
// sess.block = block
|
|
||||||
sess.recvbuf = make([]byte, mtuLimit)
|
sess.recvbuf = make([]byte, mtuLimit)
|
||||||
|
|
||||||
// cast to writebatch conn
|
// cast to writebatch conn
|
||||||
@@ -151,22 +139,6 @@ func newUDPSession(conv uint64, l *Listener, conn net.PacketConn, ownConn bool,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // FEC codec initialization
|
|
||||||
// sess.fecDecoder = newFECDecoder(dataShards, parityShards)
|
|
||||||
// if sess.block != nil {
|
|
||||||
// sess.fecEncoder = newFECEncoder(dataShards, parityShards, cryptHeaderSize)
|
|
||||||
// } else {
|
|
||||||
// sess.fecEncoder = newFECEncoder(dataShards, parityShards, 0)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // calculate additional header size introduced by FEC and encryption
|
|
||||||
// if sess.block != nil {
|
|
||||||
// sess.headerSize += cryptHeaderSize
|
|
||||||
// }
|
|
||||||
// if sess.fecEncoder != nil {
|
|
||||||
// sess.headerSize += fecHeaderSizePlus2
|
|
||||||
// }
|
|
||||||
|
|
||||||
sess.kcp = NewKCP(conv, func(buf []byte, size int) {
|
sess.kcp = NewKCP(conv, func(buf []byte, size int) {
|
||||||
if size >= IKCP_OVERHEAD+sess.headerSize {
|
if size >= IKCP_OVERHEAD+sess.headerSize {
|
||||||
sess.output(buf[:size])
|
sess.output(buf[:size])
|
||||||
@@ -370,9 +342,6 @@ func (s *UDPSession) Close() error {
|
|||||||
s.uncork()
|
s.uncork()
|
||||||
// release pending segments
|
// release pending segments
|
||||||
s.kcp.ReleaseTX()
|
s.kcp.ReleaseTX()
|
||||||
// if s.fecDecoder != nil {
|
|
||||||
// s.fecDecoder.release()
|
|
||||||
// }
|
|
||||||
s.mu.Unlock()
|
s.mu.Unlock()
|
||||||
|
|
||||||
if s.l != nil { // belongs to listener
|
if s.l != nil { // belongs to listener
|
||||||
@@ -551,26 +520,6 @@ func (s *UDPSession) SetWriteBuffer(bytes int) error {
|
|||||||
func (s *UDPSession) output(buf []byte) {
|
func (s *UDPSession) output(buf []byte) {
|
||||||
var ecc [][]byte
|
var ecc [][]byte
|
||||||
|
|
||||||
// // 1. FEC encoding
|
|
||||||
// if s.fecEncoder != nil {
|
|
||||||
// ecc = s.fecEncoder.encode(buf)
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 2&3. crc32 & encryption
|
|
||||||
// if s.block != nil {
|
|
||||||
// s.nonce.Fill(buf[:nonceSize])
|
|
||||||
// checksum := crc32.ChecksumIEEE(buf[cryptHeaderSize:])
|
|
||||||
// binary.LittleEndian.PutUint32(buf[nonceSize:], checksum)
|
|
||||||
// s.block.Encrypt(buf, buf)
|
|
||||||
//
|
|
||||||
// for k := range ecc {
|
|
||||||
// s.nonce.Fill(ecc[k][:nonceSize])
|
|
||||||
// checksum := crc32.ChecksumIEEE(ecc[k][cryptHeaderSize:])
|
|
||||||
// binary.LittleEndian.PutUint32(ecc[k][nonceSize:], checksum)
|
|
||||||
// s.block.Encrypt(ecc[k], ecc[k])
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 4. TxQueue
|
// 4. TxQueue
|
||||||
var msg ipv4.Message
|
var msg ipv4.Message
|
||||||
for i := 0; i < s.dup+1; i++ {
|
for i := 0; i < s.dup+1; i++ {
|
||||||
@@ -662,101 +611,12 @@ func (s *UDPSession) notifyWriteError(err error) {
|
|||||||
|
|
||||||
// packet input stage
|
// packet input stage
|
||||||
func (s *UDPSession) packetInput(data []byte) {
|
func (s *UDPSession) packetInput(data []byte) {
|
||||||
// decrypted := false
|
if len(data) >= IKCP_OVERHEAD {
|
||||||
// if s.block != nil && len(data) >= cryptHeaderSize {
|
|
||||||
// s.block.Decrypt(data, data)
|
|
||||||
// data = data[nonceSize:]
|
|
||||||
// checksum := crc32.ChecksumIEEE(data[crcSize:])
|
|
||||||
// if checksum == binary.LittleEndian.Uint32(data) {
|
|
||||||
// data = data[crcSize:]
|
|
||||||
// decrypted = true
|
|
||||||
// } else {
|
|
||||||
// atomic.AddUint64(&DefaultSnmp.InCsumErrors, 1)
|
|
||||||
// }
|
|
||||||
// } else if s.block == nil {
|
|
||||||
// decrypted = true
|
|
||||||
// }
|
|
||||||
|
|
||||||
decrypted := true
|
|
||||||
if decrypted && len(data) >= IKCP_OVERHEAD {
|
|
||||||
s.kcpInput(data)
|
s.kcpInput(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UDPSession) kcpInput(data []byte) {
|
func (s *UDPSession) kcpInput(data []byte) {
|
||||||
// var kcpInErrors, fecErrs, fecRecovered, fecParityShards uint64
|
|
||||||
//
|
|
||||||
// fecFlag := binary.LittleEndian.Uint16(data[8:])
|
|
||||||
// if fecFlag == typeData || fecFlag == typeParity { // 16bit kcp cmd [81-84] and frg [0-255] will not overlap with FEC type 0x00f1 0x00f2
|
|
||||||
// if len(data) >= fecHeaderSizePlus2 {
|
|
||||||
// f := fecPacket(data)
|
|
||||||
// if f.flag() == typeParity {
|
|
||||||
// fecParityShards++
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // lock
|
|
||||||
// s.mu.Lock()
|
|
||||||
// // if fecDecoder is not initialized, create one with default parameter
|
|
||||||
// if s.fecDecoder == nil {
|
|
||||||
// s.fecDecoder = newFECDecoder(1, 1)
|
|
||||||
// }
|
|
||||||
// recovers := s.fecDecoder.decode(f)
|
|
||||||
// if f.flag() == typeData {
|
|
||||||
// if ret := s.kcp.Input(data[fecHeaderSizePlus2:], true, s.ackNoDelay); ret != 0 {
|
|
||||||
// kcpInErrors++
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for _, r := range recovers {
|
|
||||||
// if len(r) >= 2 { // must be larger than 2bytes
|
|
||||||
// sz := binary.LittleEndian.Uint16(r)
|
|
||||||
// if int(sz) <= len(r) && sz >= 2 {
|
|
||||||
// if ret := s.kcp.Input(r[2:sz], false, s.ackNoDelay); ret == 0 {
|
|
||||||
// fecRecovered++
|
|
||||||
// } else {
|
|
||||||
// kcpInErrors++
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// fecErrs++
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// fecErrs++
|
|
||||||
// }
|
|
||||||
// // recycle the recovers
|
|
||||||
// xmitBuf.Put(r)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // to notify the readers to receive the data
|
|
||||||
// if n := s.kcp.PeekSize(); n > 0 {
|
|
||||||
// s.notifyReadEvent()
|
|
||||||
// }
|
|
||||||
// // to notify the writers
|
|
||||||
// waitsnd := s.kcp.WaitSnd()
|
|
||||||
// if waitsnd < int(s.kcp.snd_wnd) && waitsnd < int(s.kcp.rmt_wnd) {
|
|
||||||
// s.notifyWriteEvent()
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// s.uncork()
|
|
||||||
// s.mu.Unlock()
|
|
||||||
// } else {
|
|
||||||
// atomic.AddUint64(&DefaultSnmp.InErrs, 1)
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// s.mu.Lock()
|
|
||||||
// if ret := s.kcp.Input(data, true, s.ackNoDelay); ret != 0 {
|
|
||||||
// kcpInErrors++
|
|
||||||
// }
|
|
||||||
// if n := s.kcp.PeekSize(); n > 0 {
|
|
||||||
// s.notifyReadEvent()
|
|
||||||
// }
|
|
||||||
// waitsnd := s.kcp.WaitSnd()
|
|
||||||
// if waitsnd < int(s.kcp.snd_wnd) && waitsnd < int(s.kcp.rmt_wnd) {
|
|
||||||
// s.notifyWriteEvent()
|
|
||||||
// }
|
|
||||||
// s.uncork()
|
|
||||||
// s.mu.Unlock()
|
|
||||||
// }
|
|
||||||
|
|
||||||
var kcpInErrors uint64
|
var kcpInErrors uint64
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
if ret := s.kcp.Input(data, true, s.ackNoDelay); ret != 0 {
|
if ret := s.kcp.Input(data, true, s.ackNoDelay); ret != 0 {
|
||||||
@@ -777,75 +637,11 @@ func (s *UDPSession) kcpInput(data []byte) {
|
|||||||
if kcpInErrors > 0 {
|
if kcpInErrors > 0 {
|
||||||
atomic.AddUint64(&DefaultSnmp.KCPInErrors, kcpInErrors)
|
atomic.AddUint64(&DefaultSnmp.KCPInErrors, kcpInErrors)
|
||||||
}
|
}
|
||||||
// if fecParityShards > 0 {
|
|
||||||
// atomic.AddUint64(&DefaultSnmp.FECParityShards, fecParityShards)
|
|
||||||
// }
|
|
||||||
// if fecErrs > 0 {
|
|
||||||
// atomic.AddUint64(&DefaultSnmp.FECErrs, fecErrs)
|
|
||||||
// }
|
|
||||||
// if fecRecovered > 0 {
|
|
||||||
// atomic.AddUint64(&DefaultSnmp.FECRecovered, fecRecovered)
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 原神Enet连接控制协议
|
|
||||||
// MM MM MM MM | LL LL LL LL | HH HH HH HH | EE EE EE EE | MM MM MM MM
|
|
||||||
// MM为表示连接状态的幻数 在开头的4字节和结尾的4字节
|
|
||||||
// LL和HH分别为convId的低4字节和高4字节
|
|
||||||
// EE为Enet事件类型 4字节
|
|
||||||
|
|
||||||
// Enet协议上报结构体
|
|
||||||
type Enet struct {
|
|
||||||
Addr string
|
|
||||||
ConvId uint64
|
|
||||||
ConnType uint8
|
|
||||||
EnetType uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enet连接状态类型
|
|
||||||
const (
|
|
||||||
ConnEnetSyn = 1
|
|
||||||
ConnEnetEst = 2
|
|
||||||
ConnEnetFin = 3
|
|
||||||
ConnEnetAddrChange = 4
|
|
||||||
)
|
|
||||||
|
|
||||||
// Enet连接状态类型幻数
|
|
||||||
var MagicEnetSynHead, _ = hex.DecodeString("000000ff")
|
|
||||||
var MagicEnetSynTail, _ = hex.DecodeString("ffffffff")
|
|
||||||
var MagicEnetEstHead, _ = hex.DecodeString("00000145")
|
|
||||||
var MagicEnetEstTail, _ = hex.DecodeString("14514545")
|
|
||||||
var MagicEnetFinHead, _ = hex.DecodeString("00000194")
|
|
||||||
var MagicEnetFinTail, _ = hex.DecodeString("19419494")
|
|
||||||
|
|
||||||
// Enet事件类型
|
|
||||||
const (
|
|
||||||
EnetTimeout = 0
|
|
||||||
EnetClientClose = 1
|
|
||||||
EnetClientRebindFail = 2
|
|
||||||
EnetClientShutdown = 3
|
|
||||||
EnetServerRelogin = 4
|
|
||||||
EnetServerKick = 5
|
|
||||||
EnetServerShutdown = 6
|
|
||||||
EnetNotFoundSession = 7
|
|
||||||
EnetLoginUnfinished = 8
|
|
||||||
EnetPacketFreqTooHigh = 9
|
|
||||||
EnetPingTimeout = 10
|
|
||||||
EnetTranferFailed = 11
|
|
||||||
EnetServerKillClient = 12
|
|
||||||
EnetCheckMoveSpeed = 13
|
|
||||||
EnetAccountPasswordChange = 14
|
|
||||||
EnetClientEditorConnectKey = 987654321
|
|
||||||
EnetClientConnectKey = 1234567890
|
|
||||||
)
|
|
||||||
|
|
||||||
type (
|
type (
|
||||||
// Listener defines a server which will be waiting to accept incoming connections
|
// Listener defines a server which will be waiting to accept incoming connections
|
||||||
Listener struct {
|
Listener struct {
|
||||||
// block BlockCrypt // block encryption
|
|
||||||
// dataShards int // FEC data shard
|
|
||||||
// parityShards int // FEC parity shard
|
|
||||||
conn net.PacketConn // the underlying packet connection
|
conn net.PacketConn // the underlying packet connection
|
||||||
ownConn bool // true if we created conn internally, false if provided by caller
|
ownConn bool // true if we created conn internally, false if provided by caller
|
||||||
|
|
||||||
@@ -871,23 +667,7 @@ type (
|
|||||||
|
|
||||||
// packet input stage
|
// packet input stage
|
||||||
func (l *Listener) packetInput(data []byte, addr net.Addr, convId uint64) {
|
func (l *Listener) packetInput(data []byte, addr net.Addr, convId uint64) {
|
||||||
// decrypted := false
|
if len(data) >= IKCP_OVERHEAD {
|
||||||
// if l.block != nil && len(data) >= cryptHeaderSize {
|
|
||||||
// l.block.Decrypt(data, data)
|
|
||||||
// data = data[nonceSize:]
|
|
||||||
// checksum := crc32.ChecksumIEEE(data[crcSize:])
|
|
||||||
// if checksum == binary.LittleEndian.Uint32(data) {
|
|
||||||
// data = data[crcSize:]
|
|
||||||
// decrypted = true
|
|
||||||
// } else {
|
|
||||||
// atomic.AddUint64(&DefaultSnmp.InCsumErrors, 1)
|
|
||||||
// }
|
|
||||||
// } else if l.block == nil {
|
|
||||||
// decrypted = true
|
|
||||||
// }
|
|
||||||
|
|
||||||
decrypted := true
|
|
||||||
if decrypted && len(data) >= IKCP_OVERHEAD {
|
|
||||||
l.sessionLock.RLock()
|
l.sessionLock.RLock()
|
||||||
s, ok := l.sessions[convId]
|
s, ok := l.sessions[convId]
|
||||||
l.sessionLock.RUnlock()
|
l.sessionLock.RUnlock()
|
||||||
@@ -895,20 +675,6 @@ func (l *Listener) packetInput(data []byte, addr net.Addr, convId uint64) {
|
|||||||
var conv uint64
|
var conv uint64
|
||||||
var sn uint32
|
var sn uint32
|
||||||
convRecovered := false
|
convRecovered := false
|
||||||
// fecFlag := binary.LittleEndian.Uint16(data[8:])
|
|
||||||
// if fecFlag == typeData || fecFlag == typeParity { // 16bit kcp cmd [81-84] and frg [0-255] will not overlap with FEC type 0x00f1 0x00f2
|
|
||||||
// // packet with FEC
|
|
||||||
// if fecFlag == typeData && len(data) >= fecHeaderSizePlus2+IKCP_OVERHEAD {
|
|
||||||
// conv = binary.LittleEndian.Uint64(data[fecHeaderSizePlus2:])
|
|
||||||
// sn = binary.LittleEndian.Uint32(data[fecHeaderSizePlus2+IKCP_SN_OFFSET:])
|
|
||||||
// convRecovered = true
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// // packet without FEC
|
|
||||||
// conv = binary.LittleEndian.Uint64(data)
|
|
||||||
// sn = binary.LittleEndian.Uint32(data[IKCP_SN_OFFSET:])
|
|
||||||
// convRecovered = true
|
|
||||||
// }
|
|
||||||
|
|
||||||
// packet without FEC
|
// packet without FEC
|
||||||
conv = binary.LittleEndian.Uint64(data)
|
conv = binary.LittleEndian.Uint64(data)
|
||||||
@@ -1103,9 +869,6 @@ func serveConn(conn net.PacketConn, ownConn bool) (*Listener, error) {
|
|||||||
l.chAccepts = make(chan *UDPSession, acceptBacklog)
|
l.chAccepts = make(chan *UDPSession, acceptBacklog)
|
||||||
l.chSessionClosed = make(chan net.Addr)
|
l.chSessionClosed = make(chan net.Addr)
|
||||||
l.die = make(chan struct{})
|
l.die = make(chan struct{})
|
||||||
// l.dataShards = dataShards
|
|
||||||
// l.parityShards = parityShards
|
|
||||||
// l.block = block
|
|
||||||
l.chSocketReadError = make(chan struct{})
|
l.chSocketReadError = make(chan struct{})
|
||||||
l.EnetNotify = make(chan *Enet, 1000)
|
l.EnetNotify = make(chan *Enet, 1000)
|
||||||
go l.monitor()
|
go l.monitor()
|
||||||
@@ -1138,9 +901,36 @@ func DialWithOptions(raddr string) (*UDPSession, error) {
|
|||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var convid uint64
|
hsconn, err := net.DialUDP(network, nil, udpaddr)
|
||||||
binary.Read(rand.Reader, binary.LittleEndian, &convid)
|
if err != nil {
|
||||||
return newUDPSession(convid, nil, conn, true, udpaddr), nil
|
return nil, err
|
||||||
|
}
|
||||||
|
enet := &Enet{
|
||||||
|
Addr: raddr,
|
||||||
|
ConvId: 0,
|
||||||
|
ConnType: ConnEnetSyn,
|
||||||
|
EnetType: EnetClientConnectKey,
|
||||||
|
}
|
||||||
|
data := BuildEnet(enet.ConnType, enet.EnetType, enet.ConvId)
|
||||||
|
_, err = hsconn.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
buf := make([]byte, mtuLimit)
|
||||||
|
n, addr, err := hsconn.ReadFrom(buf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if addr.String() != raddr {
|
||||||
|
return nil, errors.New("recv packet remote addr not match")
|
||||||
|
}
|
||||||
|
udpPayload := buf[:n]
|
||||||
|
connType, enetType, conv, err := ParseEnet(udpPayload)
|
||||||
|
if err != nil || connType != ConnEnetEst || enetType != EnetClientConnectKey {
|
||||||
|
return nil, errors.New("recv packet format error")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newUDPSession(conv, nil, conn, true, udpaddr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConn3 establishes a session and talks KCP protocol over a packet connection.
|
// NewConn3 establishes a session and talks KCP protocol over a packet connection.
|
||||||
@@ -7,14 +7,12 @@ import (
|
|||||||
|
|
||||||
// Snmp defines network statistics indicator
|
// Snmp defines network statistics indicator
|
||||||
type Snmp struct {
|
type Snmp struct {
|
||||||
BytesSent uint64 // bytes sent from upper level
|
BytesSent uint64 // bytes sent from upper level
|
||||||
BytesReceived uint64 // bytes received to upper level
|
BytesReceived uint64 // bytes received to upper level
|
||||||
MaxConn uint64 // max number of connections ever reached
|
MaxConn uint64 // max number of connections ever reached
|
||||||
ActiveOpens uint64 // accumulated active open connections
|
ActiveOpens uint64 // accumulated active open connections
|
||||||
PassiveOpens uint64 // accumulated passive open connections
|
PassiveOpens uint64 // accumulated passive open connections
|
||||||
CurrEstab uint64 // current number of established connections
|
CurrEstab uint64 // current number of established connections
|
||||||
// InErrs uint64 // UDP read errors reported from net.PacketConn
|
|
||||||
// InCsumErrors uint64 // checksum errors from CRC32
|
|
||||||
KCPInErrors uint64 // packet iput errors reported from KCP
|
KCPInErrors uint64 // packet iput errors reported from KCP
|
||||||
InPkts uint64 // incoming packets count
|
InPkts uint64 // incoming packets count
|
||||||
OutPkts uint64 // outgoing packets count
|
OutPkts uint64 // outgoing packets count
|
||||||
@@ -27,10 +25,6 @@ type Snmp struct {
|
|||||||
EarlyRetransSegs uint64 // accmulated early retransmitted segments
|
EarlyRetransSegs uint64 // accmulated early retransmitted segments
|
||||||
LostSegs uint64 // number of segs inferred as lost
|
LostSegs uint64 // number of segs inferred as lost
|
||||||
RepeatSegs uint64 // number of segs duplicated
|
RepeatSegs uint64 // number of segs duplicated
|
||||||
// FECRecovered uint64 // correct packets recovered from FEC
|
|
||||||
// FECErrs uint64 // incorrect packets recovered from FEC
|
|
||||||
// FECParityShards uint64 // FEC segments received
|
|
||||||
// FECShortShards uint64 // number of data shards that's not enough for recovery
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSnmp() *Snmp {
|
func newSnmp() *Snmp {
|
||||||
@@ -46,8 +40,6 @@ func (s *Snmp) Header() []string {
|
|||||||
"ActiveOpens",
|
"ActiveOpens",
|
||||||
"PassiveOpens",
|
"PassiveOpens",
|
||||||
"CurrEstab",
|
"CurrEstab",
|
||||||
// "InErrs",
|
|
||||||
// "InCsumErrors",
|
|
||||||
"KCPInErrors",
|
"KCPInErrors",
|
||||||
"InPkts",
|
"InPkts",
|
||||||
"OutPkts",
|
"OutPkts",
|
||||||
@@ -60,10 +52,6 @@ func (s *Snmp) Header() []string {
|
|||||||
"EarlyRetransSegs",
|
"EarlyRetransSegs",
|
||||||
"LostSegs",
|
"LostSegs",
|
||||||
"RepeatSegs",
|
"RepeatSegs",
|
||||||
// "FECParityShards",
|
|
||||||
// "FECErrs",
|
|
||||||
// "FECRecovered",
|
|
||||||
// "FECShortShards",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,8 +65,6 @@ func (s *Snmp) ToSlice() []string {
|
|||||||
fmt.Sprint(snmp.ActiveOpens),
|
fmt.Sprint(snmp.ActiveOpens),
|
||||||
fmt.Sprint(snmp.PassiveOpens),
|
fmt.Sprint(snmp.PassiveOpens),
|
||||||
fmt.Sprint(snmp.CurrEstab),
|
fmt.Sprint(snmp.CurrEstab),
|
||||||
// fmt.Sprint(snmp.InErrs),
|
|
||||||
// fmt.Sprint(snmp.InCsumErrors),
|
|
||||||
fmt.Sprint(snmp.KCPInErrors),
|
fmt.Sprint(snmp.KCPInErrors),
|
||||||
fmt.Sprint(snmp.InPkts),
|
fmt.Sprint(snmp.InPkts),
|
||||||
fmt.Sprint(snmp.OutPkts),
|
fmt.Sprint(snmp.OutPkts),
|
||||||
@@ -91,10 +77,6 @@ func (s *Snmp) ToSlice() []string {
|
|||||||
fmt.Sprint(snmp.EarlyRetransSegs),
|
fmt.Sprint(snmp.EarlyRetransSegs),
|
||||||
fmt.Sprint(snmp.LostSegs),
|
fmt.Sprint(snmp.LostSegs),
|
||||||
fmt.Sprint(snmp.RepeatSegs),
|
fmt.Sprint(snmp.RepeatSegs),
|
||||||
// fmt.Sprint(snmp.FECParityShards),
|
|
||||||
// fmt.Sprint(snmp.FECErrs),
|
|
||||||
// fmt.Sprint(snmp.FECRecovered),
|
|
||||||
// fmt.Sprint(snmp.FECShortShards),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +89,6 @@ func (s *Snmp) Copy() *Snmp {
|
|||||||
d.ActiveOpens = atomic.LoadUint64(&s.ActiveOpens)
|
d.ActiveOpens = atomic.LoadUint64(&s.ActiveOpens)
|
||||||
d.PassiveOpens = atomic.LoadUint64(&s.PassiveOpens)
|
d.PassiveOpens = atomic.LoadUint64(&s.PassiveOpens)
|
||||||
d.CurrEstab = atomic.LoadUint64(&s.CurrEstab)
|
d.CurrEstab = atomic.LoadUint64(&s.CurrEstab)
|
||||||
// d.InErrs = atomic.LoadUint64(&s.InErrs)
|
|
||||||
// d.InCsumErrors = atomic.LoadUint64(&s.InCsumErrors)
|
|
||||||
d.KCPInErrors = atomic.LoadUint64(&s.KCPInErrors)
|
d.KCPInErrors = atomic.LoadUint64(&s.KCPInErrors)
|
||||||
d.InPkts = atomic.LoadUint64(&s.InPkts)
|
d.InPkts = atomic.LoadUint64(&s.InPkts)
|
||||||
d.OutPkts = atomic.LoadUint64(&s.OutPkts)
|
d.OutPkts = atomic.LoadUint64(&s.OutPkts)
|
||||||
@@ -121,10 +101,6 @@ func (s *Snmp) Copy() *Snmp {
|
|||||||
d.EarlyRetransSegs = atomic.LoadUint64(&s.EarlyRetransSegs)
|
d.EarlyRetransSegs = atomic.LoadUint64(&s.EarlyRetransSegs)
|
||||||
d.LostSegs = atomic.LoadUint64(&s.LostSegs)
|
d.LostSegs = atomic.LoadUint64(&s.LostSegs)
|
||||||
d.RepeatSegs = atomic.LoadUint64(&s.RepeatSegs)
|
d.RepeatSegs = atomic.LoadUint64(&s.RepeatSegs)
|
||||||
// d.FECParityShards = atomic.LoadUint64(&s.FECParityShards)
|
|
||||||
// d.FECErrs = atomic.LoadUint64(&s.FECErrs)
|
|
||||||
// d.FECRecovered = atomic.LoadUint64(&s.FECRecovered)
|
|
||||||
// d.FECShortShards = atomic.LoadUint64(&s.FECShortShards)
|
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,8 +112,6 @@ func (s *Snmp) Reset() {
|
|||||||
atomic.StoreUint64(&s.ActiveOpens, 0)
|
atomic.StoreUint64(&s.ActiveOpens, 0)
|
||||||
atomic.StoreUint64(&s.PassiveOpens, 0)
|
atomic.StoreUint64(&s.PassiveOpens, 0)
|
||||||
atomic.StoreUint64(&s.CurrEstab, 0)
|
atomic.StoreUint64(&s.CurrEstab, 0)
|
||||||
// atomic.StoreUint64(&s.InErrs, 0)
|
|
||||||
// atomic.StoreUint64(&s.InCsumErrors, 0)
|
|
||||||
atomic.StoreUint64(&s.KCPInErrors, 0)
|
atomic.StoreUint64(&s.KCPInErrors, 0)
|
||||||
atomic.StoreUint64(&s.InPkts, 0)
|
atomic.StoreUint64(&s.InPkts, 0)
|
||||||
atomic.StoreUint64(&s.OutPkts, 0)
|
atomic.StoreUint64(&s.OutPkts, 0)
|
||||||
@@ -150,10 +124,6 @@ func (s *Snmp) Reset() {
|
|||||||
atomic.StoreUint64(&s.EarlyRetransSegs, 0)
|
atomic.StoreUint64(&s.EarlyRetransSegs, 0)
|
||||||
atomic.StoreUint64(&s.LostSegs, 0)
|
atomic.StoreUint64(&s.LostSegs, 0)
|
||||||
atomic.StoreUint64(&s.RepeatSegs, 0)
|
atomic.StoreUint64(&s.RepeatSegs, 0)
|
||||||
// atomic.StoreUint64(&s.FECParityShards, 0)
|
|
||||||
// atomic.StoreUint64(&s.FECErrs, 0)
|
|
||||||
// atomic.StoreUint64(&s.FECRecovered, 0)
|
|
||||||
// atomic.StoreUint64(&s.FECShortShards, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultSnmp is the global KCP connection statistics collector
|
// DefaultSnmp is the global KCP connection statistics collector
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
package kcp
|
package kcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
"net"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
@@ -21,12 +19,24 @@ func (s *UDPSession) defaultReadLoop() {
|
|||||||
if src == "" { // set source address
|
if src == "" { // set source address
|
||||||
src = addr.String()
|
src = addr.String()
|
||||||
} else if addr.String() != src {
|
} else if addr.String() != src {
|
||||||
// atomic.AddUint64(&DefaultSnmp.InErrs, 1)
|
|
||||||
// continue
|
|
||||||
s.remote = addr
|
s.remote = addr
|
||||||
src = addr.String()
|
src = addr.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if n == 20 {
|
||||||
|
connType, _, conv, err := ParseEnet(udpPayload)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if conv != s.GetConv() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if connType == ConnEnetFin {
|
||||||
|
s.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.packetInput(udpPayload)
|
s.packetInput(udpPayload)
|
||||||
} else {
|
} else {
|
||||||
s.notifyReadError(errors.WithStack(err))
|
s.notifyReadError(errors.WithStack(err))
|
||||||
@@ -42,27 +52,13 @@ func (l *Listener) defaultMonitor() {
|
|||||||
udpPayload := buf[:n]
|
udpPayload := buf[:n]
|
||||||
var convId uint64 = 0
|
var convId uint64 = 0
|
||||||
if n == 20 {
|
if n == 20 {
|
||||||
// 原神KCP的Enet协议
|
connType, enetType, conv, err := ParseEnet(udpPayload)
|
||||||
// 提取convId
|
if err != nil {
|
||||||
convId += uint64(udpPayload[4]) << 24
|
continue
|
||||||
convId += uint64(udpPayload[5]) << 16
|
}
|
||||||
convId += uint64(udpPayload[6]) << 8
|
convId = conv
|
||||||
convId += uint64(udpPayload[7]) << 0
|
switch connType {
|
||||||
convId += uint64(udpPayload[8]) << 56
|
case ConnEnetSyn:
|
||||||
convId += uint64(udpPayload[9]) << 48
|
|
||||||
convId += uint64(udpPayload[10]) << 40
|
|
||||||
convId += uint64(udpPayload[11]) << 32
|
|
||||||
// 提取Enet协议头部和尾部幻数
|
|
||||||
udpPayloadEnetHead := udpPayload[:4]
|
|
||||||
udpPayloadEnetTail := udpPayload[len(udpPayload)-4:]
|
|
||||||
// 提取Enet协议类型
|
|
||||||
enetTypeData := udpPayload[12:16]
|
|
||||||
enetTypeDataBuffer := bytes.NewBuffer(enetTypeData)
|
|
||||||
var enetType uint32
|
|
||||||
_ = binary.Read(enetTypeDataBuffer, binary.BigEndian, &enetType)
|
|
||||||
equalHead := bytes.Compare(udpPayloadEnetHead, MagicEnetSynHead)
|
|
||||||
equalTail := bytes.Compare(udpPayloadEnetTail, MagicEnetSynTail)
|
|
||||||
if equalHead == 0 && equalTail == 0 {
|
|
||||||
// 客户端前置握手获取conv
|
// 客户端前置握手获取conv
|
||||||
l.EnetNotify <- &Enet{
|
l.EnetNotify <- &Enet{
|
||||||
Addr: from.String(),
|
Addr: from.String(),
|
||||||
@@ -70,11 +66,7 @@ func (l *Listener) defaultMonitor() {
|
|||||||
ConnType: ConnEnetSyn,
|
ConnType: ConnEnetSyn,
|
||||||
EnetType: enetType,
|
EnetType: enetType,
|
||||||
}
|
}
|
||||||
continue
|
case ConnEnetEst:
|
||||||
}
|
|
||||||
equalHead = bytes.Compare(udpPayloadEnetHead, MagicEnetEstHead)
|
|
||||||
equalTail = bytes.Compare(udpPayloadEnetTail, MagicEnetEstTail)
|
|
||||||
if equalHead == 0 && equalTail == 0 {
|
|
||||||
// 连接建立
|
// 连接建立
|
||||||
l.EnetNotify <- &Enet{
|
l.EnetNotify <- &Enet{
|
||||||
Addr: from.String(),
|
Addr: from.String(),
|
||||||
@@ -82,11 +74,7 @@ func (l *Listener) defaultMonitor() {
|
|||||||
ConnType: ConnEnetEst,
|
ConnType: ConnEnetEst,
|
||||||
EnetType: enetType,
|
EnetType: enetType,
|
||||||
}
|
}
|
||||||
continue
|
case ConnEnetFin:
|
||||||
}
|
|
||||||
equalHead = bytes.Compare(udpPayloadEnetHead, MagicEnetFinHead)
|
|
||||||
equalTail = bytes.Compare(udpPayloadEnetTail, MagicEnetFinTail)
|
|
||||||
if equalHead == 0 && equalTail == 0 {
|
|
||||||
// 连接断开
|
// 连接断开
|
||||||
l.EnetNotify <- &Enet{
|
l.EnetNotify <- &Enet{
|
||||||
Addr: from.String(),
|
Addr: from.String(),
|
||||||
@@ -94,6 +82,7 @@ func (l *Listener) defaultMonitor() {
|
|||||||
ConnType: ConnEnetFin,
|
ConnType: ConnEnetFin,
|
||||||
EnetType: enetType,
|
EnetType: enetType,
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -129,61 +118,6 @@ func (l *Listener) defaultMonitor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildEnet(connType uint8, enetType uint32, conv uint64) []byte {
|
|
||||||
data := make([]byte, 20)
|
|
||||||
if connType == ConnEnetSyn {
|
|
||||||
copy(data[0:4], MagicEnetSynHead)
|
|
||||||
copy(data[16:20], MagicEnetSynTail)
|
|
||||||
} else if connType == ConnEnetEst {
|
|
||||||
copy(data[0:4], MagicEnetEstHead)
|
|
||||||
copy(data[16:20], MagicEnetEstTail)
|
|
||||||
} else if connType == ConnEnetFin {
|
|
||||||
copy(data[0:4], MagicEnetFinHead)
|
|
||||||
copy(data[16:20], MagicEnetFinTail)
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
// conv的高四个字节和低四个字节分开
|
|
||||||
// 例如 00 00 01 45 | LL LL LL LL | HH HH HH HH | 49 96 02 d2 | 14 51 45 45
|
|
||||||
data[4] = uint8(conv >> 24)
|
|
||||||
data[5] = uint8(conv >> 16)
|
|
||||||
data[6] = uint8(conv >> 8)
|
|
||||||
data[7] = uint8(conv >> 0)
|
|
||||||
data[8] = uint8(conv >> 56)
|
|
||||||
data[9] = uint8(conv >> 48)
|
|
||||||
data[10] = uint8(conv >> 40)
|
|
||||||
data[11] = uint8(conv >> 32)
|
|
||||||
// Enet
|
|
||||||
data[12] = uint8(enetType >> 24)
|
|
||||||
data[13] = uint8(enetType >> 16)
|
|
||||||
data[14] = uint8(enetType >> 8)
|
|
||||||
data[15] = uint8(enetType >> 0)
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l *Listener) defaultSendEnetNotifyToClient(enet *Enet) {
|
|
||||||
remoteAddr, err := net.ResolveUDPAddr("udp", enet.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
data := buildEnet(enet.ConnType, enet.EnetType, enet.ConvId)
|
|
||||||
if data == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
_, _ = l.conn.WriteTo(data, remoteAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *UDPSession) defaultSendEnetNotify(enet *Enet) {
|
|
||||||
data := buildEnet(enet.ConnType, enet.EnetType, s.GetConv())
|
|
||||||
if data == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.defaultTx([]ipv4.Message{{
|
|
||||||
Buffers: [][]byte{data},
|
|
||||||
Addr: s.remote,
|
|
||||||
}})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *UDPSession) defaultTx(txqueue []ipv4.Message) {
|
func (s *UDPSession) defaultTx(txqueue []ipv4.Message) {
|
||||||
nbytes := 0
|
nbytes := 0
|
||||||
npkts := 0
|
npkts := 0
|
||||||
@@ -199,3 +133,26 @@ func (s *UDPSession) defaultTx(txqueue []ipv4.Message) {
|
|||||||
atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
|
atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
|
||||||
atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
|
atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Listener) defaultSendEnetNotifyToPeer(enet *Enet) {
|
||||||
|
remoteAddr, err := net.ResolveUDPAddr("udp", enet.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data := BuildEnet(enet.ConnType, enet.EnetType, enet.ConvId)
|
||||||
|
if data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = l.conn.WriteTo(data, remoteAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UDPSession) defaultSendEnetNotifyToPeer(enet *Enet) {
|
||||||
|
data := BuildEnet(enet.ConnType, enet.EnetType, s.GetConv())
|
||||||
|
if data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.defaultTx([]ipv4.Message{{
|
||||||
|
Buffers: [][]byte{data},
|
||||||
|
Addr: s.remote,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,8 +4,6 @@
|
|||||||
package kcp
|
package kcp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/binary"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@@ -15,6 +13,15 @@ import (
|
|||||||
"golang.org/x/net/ipv6"
|
"golang.org/x/net/ipv6"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
batchSize = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
type batchConn interface {
|
||||||
|
WriteBatch(ms []ipv4.Message, flags int) (int, error)
|
||||||
|
ReadBatch(ms []ipv4.Message, flags int) (int, error)
|
||||||
|
}
|
||||||
|
|
||||||
// the read loop for a client session
|
// the read loop for a client session
|
||||||
func (s *UDPSession) readLoop() {
|
func (s *UDPSession) readLoop() {
|
||||||
// default version
|
// default version
|
||||||
@@ -39,14 +46,26 @@ func (s *UDPSession) readLoop() {
|
|||||||
if src == "" { // set source address if nil
|
if src == "" { // set source address if nil
|
||||||
src = msg.Addr.String()
|
src = msg.Addr.String()
|
||||||
} else if msg.Addr.String() != src {
|
} else if msg.Addr.String() != src {
|
||||||
// atomic.AddUint64(&DefaultSnmp.InErrs, 1)
|
|
||||||
// continue
|
|
||||||
s.remote = msg.Addr
|
s.remote = msg.Addr
|
||||||
src = msg.Addr.String()
|
src = msg.Addr.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
udpPayload := msg.Buffers[0][:msg.N]
|
udpPayload := msg.Buffers[0][:msg.N]
|
||||||
|
|
||||||
|
if msg.N == 20 {
|
||||||
|
connType, _, conv, err := ParseEnet(udpPayload)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if conv != s.GetConv() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if connType == ConnEnetFin {
|
||||||
|
s.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// source and size has validated
|
// source and size has validated
|
||||||
s.packetInput(udpPayload)
|
s.packetInput(udpPayload)
|
||||||
}
|
}
|
||||||
@@ -101,27 +120,13 @@ func (l *Listener) monitor() {
|
|||||||
udpPayload := msg.Buffers[0][:msg.N]
|
udpPayload := msg.Buffers[0][:msg.N]
|
||||||
var convId uint64 = 0
|
var convId uint64 = 0
|
||||||
if msg.N == 20 {
|
if msg.N == 20 {
|
||||||
// 原神KCP的Enet协议
|
connType, enetType, conv, err := ParseEnet(udpPayload)
|
||||||
// 提取convId
|
if err != nil {
|
||||||
convId += uint64(udpPayload[4]) << 24
|
continue
|
||||||
convId += uint64(udpPayload[5]) << 16
|
}
|
||||||
convId += uint64(udpPayload[6]) << 8
|
convId = conv
|
||||||
convId += uint64(udpPayload[7]) << 0
|
switch connType {
|
||||||
convId += uint64(udpPayload[8]) << 56
|
case ConnEnetSyn:
|
||||||
convId += uint64(udpPayload[9]) << 48
|
|
||||||
convId += uint64(udpPayload[10]) << 40
|
|
||||||
convId += uint64(udpPayload[11]) << 32
|
|
||||||
// 提取Enet协议头部和尾部幻数
|
|
||||||
udpPayloadEnetHead := udpPayload[:4]
|
|
||||||
udpPayloadEnetTail := udpPayload[len(udpPayload)-4:]
|
|
||||||
// 提取Enet协议类型
|
|
||||||
enetTypeData := udpPayload[12:16]
|
|
||||||
enetTypeDataBuffer := bytes.NewBuffer(enetTypeData)
|
|
||||||
var enetType uint32
|
|
||||||
_ = binary.Read(enetTypeDataBuffer, binary.BigEndian, &enetType)
|
|
||||||
equalHead := bytes.Compare(udpPayloadEnetHead, MagicEnetSynHead)
|
|
||||||
equalTail := bytes.Compare(udpPayloadEnetTail, MagicEnetSynTail)
|
|
||||||
if equalHead == 0 && equalTail == 0 {
|
|
||||||
// 客户端前置握手获取conv
|
// 客户端前置握手获取conv
|
||||||
l.EnetNotify <- &Enet{
|
l.EnetNotify <- &Enet{
|
||||||
Addr: msg.Addr.String(),
|
Addr: msg.Addr.String(),
|
||||||
@@ -129,11 +134,7 @@ func (l *Listener) monitor() {
|
|||||||
ConnType: ConnEnetSyn,
|
ConnType: ConnEnetSyn,
|
||||||
EnetType: enetType,
|
EnetType: enetType,
|
||||||
}
|
}
|
||||||
continue
|
case ConnEnetEst:
|
||||||
}
|
|
||||||
equalHead = bytes.Compare(udpPayloadEnetHead, MagicEnetEstHead)
|
|
||||||
equalTail = bytes.Compare(udpPayloadEnetTail, MagicEnetEstTail)
|
|
||||||
if equalHead == 0 && equalTail == 0 {
|
|
||||||
// 连接建立
|
// 连接建立
|
||||||
l.EnetNotify <- &Enet{
|
l.EnetNotify <- &Enet{
|
||||||
Addr: msg.Addr.String(),
|
Addr: msg.Addr.String(),
|
||||||
@@ -141,11 +142,7 @@ func (l *Listener) monitor() {
|
|||||||
ConnType: ConnEnetEst,
|
ConnType: ConnEnetEst,
|
||||||
EnetType: enetType,
|
EnetType: enetType,
|
||||||
}
|
}
|
||||||
continue
|
case ConnEnetFin:
|
||||||
}
|
|
||||||
equalHead = bytes.Compare(udpPayloadEnetHead, MagicEnetFinHead)
|
|
||||||
equalTail = bytes.Compare(udpPayloadEnetTail, MagicEnetFinTail)
|
|
||||||
if equalHead == 0 && equalTail == 0 {
|
|
||||||
// 连接断开
|
// 连接断开
|
||||||
l.EnetNotify <- &Enet{
|
l.EnetNotify <- &Enet{
|
||||||
Addr: msg.Addr.String(),
|
Addr: msg.Addr.String(),
|
||||||
@@ -153,6 +150,7 @@ func (l *Listener) monitor() {
|
|||||||
ConnType: ConnEnetFin,
|
ConnType: ConnEnetFin,
|
||||||
EnetType: enetType,
|
EnetType: enetType,
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -200,55 +198,6 @@ func (l *Listener) monitor() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Listener) SendEnetNotifyToClient(enet *Enet) {
|
|
||||||
var xconn batchConn
|
|
||||||
_, ok := l.conn.(*net.UDPConn)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
localAddr, err := net.ResolveUDPAddr("udp", l.conn.LocalAddr().String())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if localAddr.IP.To4() != nil {
|
|
||||||
xconn = ipv4.NewPacketConn(l.conn)
|
|
||||||
} else {
|
|
||||||
xconn = ipv6.NewPacketConn(l.conn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// default version
|
|
||||||
if xconn == nil {
|
|
||||||
l.defaultSendEnetNotifyToClient(enet)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
remoteAddr, err := net.ResolveUDPAddr("udp", enet.Addr)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
data := buildEnet(enet.ConnType, enet.EnetType, enet.ConvId)
|
|
||||||
if data == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, _ = xconn.WriteBatch([]ipv4.Message{{
|
|
||||||
Buffers: [][]byte{data},
|
|
||||||
Addr: remoteAddr,
|
|
||||||
}}, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *UDPSession) SendEnetNotify(enet *Enet) {
|
|
||||||
data := buildEnet(enet.ConnType, enet.EnetType, s.GetConv())
|
|
||||||
if data == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
s.tx([]ipv4.Message{{
|
|
||||||
Buffers: [][]byte{data},
|
|
||||||
Addr: s.remote,
|
|
||||||
}})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *UDPSession) tx(txqueue []ipv4.Message) {
|
func (s *UDPSession) tx(txqueue []ipv4.Message) {
|
||||||
// default version
|
// default version
|
||||||
if s.xconn == nil || s.xconnWriteError != nil {
|
if s.xconn == nil || s.xconnWriteError != nil {
|
||||||
@@ -287,3 +236,52 @@ func (s *UDPSession) tx(txqueue []ipv4.Message) {
|
|||||||
atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
|
atomic.AddUint64(&DefaultSnmp.OutPkts, uint64(npkts))
|
||||||
atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
|
atomic.AddUint64(&DefaultSnmp.OutBytes, uint64(nbytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *Listener) SendEnetNotifyToPeer(enet *Enet) {
|
||||||
|
var xconn batchConn
|
||||||
|
_, ok := l.conn.(*net.UDPConn)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
localAddr, err := net.ResolveUDPAddr("udp", l.conn.LocalAddr().String())
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if localAddr.IP.To4() != nil {
|
||||||
|
xconn = ipv4.NewPacketConn(l.conn)
|
||||||
|
} else {
|
||||||
|
xconn = ipv6.NewPacketConn(l.conn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// default version
|
||||||
|
if xconn == nil {
|
||||||
|
l.defaultSendEnetNotifyToPeer(enet)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
remoteAddr, err := net.ResolveUDPAddr("udp", enet.Addr)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data := BuildEnet(enet.ConnType, enet.EnetType, enet.ConvId)
|
||||||
|
if data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = xconn.WriteBatch([]ipv4.Message{{
|
||||||
|
Buffers: [][]byte{data},
|
||||||
|
Addr: remoteAddr,
|
||||||
|
}}, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UDPSession) SendEnetNotifyToPeer(enet *Enet) {
|
||||||
|
data := BuildEnet(enet.ConnType, enet.EnetType, s.GetConv())
|
||||||
|
if data == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.tx([]ipv4.Message{{
|
||||||
|
Buffers: [][]byte{data},
|
||||||
|
Addr: s.remote,
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,12 +15,12 @@ func (l *Listener) monitor() {
|
|||||||
l.defaultMonitor()
|
l.defaultMonitor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *Listener) SendEnetNotifyToClient(enet *Enet) {
|
func (l *Listener) SendEnetNotifyToPeer(enet *Enet) {
|
||||||
l.defaultSendEnetNotifyToClient(enet)
|
l.defaultSendEnetNotifyToPeer(enet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UDPSession) SendEnetNotify(enet *Enet) {
|
func (s *UDPSession) SendEnetNotifyToPeer(enet *Enet) {
|
||||||
s.defaultSendEnetNotify(enet)
|
s.defaultSendEnetNotifyToPeer(enet)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *UDPSession) tx(txqueue []ipv4.Message) {
|
func (s *UDPSession) tx(txqueue []ipv4.Message) {
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ func (k *KcpConnectManager) enetHandle(listener *kcp.Listener) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
listener.SendEnetNotifyToClient(&kcp.Enet{
|
listener.SendEnetNotifyToPeer(&kcp.Enet{
|
||||||
Addr: enetNotify.Addr,
|
Addr: enetNotify.Addr,
|
||||||
ConvId: conv,
|
ConvId: conv,
|
||||||
ConnType: kcp.ConnEnetEst,
|
ConnType: kcp.ConnEnetEst,
|
||||||
@@ -251,7 +251,7 @@ func (k *KcpConnectManager) enetHandle(listener *kcp.Listener) {
|
|||||||
logger.Error("session not exist, conv: %v", enetNotify.ConvId)
|
logger.Error("session not exist, conv: %v", enetNotify.ConvId)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
session.conn.SendEnetNotify(&kcp.Enet{
|
session.conn.SendEnetNotifyToPeer(&kcp.Enet{
|
||||||
ConnType: kcp.ConnEnetFin,
|
ConnType: kcp.ConnEnetFin,
|
||||||
EnetType: enetNotify.EnetType,
|
EnetType: enetNotify.EnetType,
|
||||||
})
|
})
|
||||||
@@ -404,7 +404,7 @@ func (k *KcpConnectManager) closeKcpConn(session *Session, enetType uint32) {
|
|||||||
// 关闭连接
|
// 关闭连接
|
||||||
err := conn.Close()
|
err := conn.Close()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
conn.SendEnetNotify(&kcp.Enet{
|
conn.SendEnetNotifyToPeer(&kcp.Enet{
|
||||||
ConnType: kcp.ConnEnetFin,
|
ConnType: kcp.ConnEnetFin,
|
||||||
EnetType: enetType,
|
EnetType: enetType,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -143,7 +143,9 @@ func AccountLogin(url string, account string, password string) (*AccountInfo, er
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
comboTokenReq := &api.ComboTokenReq{
|
comboTokenReq := &api.ComboTokenReq{
|
||||||
Data: string(loginTokenDataJson),
|
AppID: 4,
|
||||||
|
ChannelID: 1,
|
||||||
|
Data: string(loginTokenDataJson),
|
||||||
}
|
}
|
||||||
logger.Info("http post url: %v", url+"/hk4e_global/combo/granter/login/v2/login")
|
logger.Info("http post url: %v", url+"/hk4e_global/combo/granter/login/v2/login")
|
||||||
comboTokenRsp, err := httpclient.PostJson[api.ComboTokenRsp](url+"/hk4e_global/combo/granter/login/v2/login", comboTokenReq, "")
|
comboTokenRsp, err := httpclient.PostJson[api.ComboTokenRsp](url+"/hk4e_global/combo/granter/login/v2/login", comboTokenReq, "")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package net
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"hk4e/gate/client_proto"
|
||||||
"hk4e/gate/kcp"
|
"hk4e/gate/kcp"
|
||||||
hk4egatenet "hk4e/gate/net"
|
hk4egatenet "hk4e/gate/net"
|
||||||
"hk4e/pkg/logger"
|
"hk4e/pkg/logger"
|
||||||
@@ -10,10 +11,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
Conn *kcp.UDPSession
|
Conn *kcp.UDPSession
|
||||||
XorKey []byte
|
XorKey []byte
|
||||||
SendChan chan *hk4egatenet.ProtoMsg
|
SendChan chan *hk4egatenet.ProtoMsg
|
||||||
RecvChan chan *hk4egatenet.ProtoMsg
|
RecvChan chan *hk4egatenet.ProtoMsg
|
||||||
|
ServerCmdProtoMap *cmd.CmdProtoMap
|
||||||
|
ClientCmdProtoMap *client_proto.ClientCmdProtoMap
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSession(gateAddr string, dispatchKey []byte, localPort int) (*Session, error) {
|
func NewSession(gateAddr string, dispatchKey []byte, localPort int) (*Session, error) {
|
||||||
@@ -27,13 +30,13 @@ func NewSession(gateAddr string, dispatchKey []byte, localPort int) (*Session, e
|
|||||||
}
|
}
|
||||||
conn.SetACKNoDelay(true)
|
conn.SetACKNoDelay(true)
|
||||||
conn.SetWriteDelay(false)
|
conn.SetWriteDelay(false)
|
||||||
sendChan := make(chan *hk4egatenet.ProtoMsg, 1000)
|
|
||||||
recvChan := make(chan *hk4egatenet.ProtoMsg, 1000)
|
|
||||||
r := &Session{
|
r := &Session{
|
||||||
Conn: conn,
|
Conn: conn,
|
||||||
XorKey: dispatchKey,
|
XorKey: dispatchKey,
|
||||||
SendChan: sendChan,
|
SendChan: make(chan *hk4egatenet.ProtoMsg, 1000),
|
||||||
RecvChan: recvChan,
|
RecvChan: make(chan *hk4egatenet.ProtoMsg, 1000),
|
||||||
|
ServerCmdProtoMap: cmd.NewCmdProtoMap(),
|
||||||
|
ClientCmdProtoMap: client_proto.NewClientCmdProtoMap(),
|
||||||
}
|
}
|
||||||
go r.recvHandle()
|
go r.recvHandle()
|
||||||
go r.sendHandle()
|
go r.sendHandle()
|
||||||
@@ -58,7 +61,7 @@ func (s *Session) recvHandle() {
|
|||||||
kcpMsgList := make([]*hk4egatenet.KcpMsg, 0)
|
kcpMsgList := make([]*hk4egatenet.KcpMsg, 0)
|
||||||
hk4egatenet.DecodeBinToPayload(recvData, &dataBuf, convId, &kcpMsgList, s.XorKey)
|
hk4egatenet.DecodeBinToPayload(recvData, &dataBuf, convId, &kcpMsgList, s.XorKey)
|
||||||
for _, v := range kcpMsgList {
|
for _, v := range kcpMsgList {
|
||||||
protoMsgList := hk4egatenet.ProtoDecode(v, cmd.NewCmdProtoMap(), nil)
|
protoMsgList := hk4egatenet.ProtoDecode(v, s.ServerCmdProtoMap, s.ClientCmdProtoMap)
|
||||||
for _, vv := range protoMsgList {
|
for _, vv := range protoMsgList {
|
||||||
s.RecvChan <- vv
|
s.RecvChan <- vv
|
||||||
}
|
}
|
||||||
@@ -77,7 +80,7 @@ func (s *Session) sendHandle() {
|
|||||||
_ = conn.Close()
|
_ = conn.Close()
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
kcpMsg := hk4egatenet.ProtoEncode(protoMsg, cmd.NewCmdProtoMap(), nil)
|
kcpMsg := hk4egatenet.ProtoEncode(protoMsg, s.ServerCmdProtoMap, s.ClientCmdProtoMap)
|
||||||
if kcpMsg == nil {
|
if kcpMsg == nil {
|
||||||
logger.Error("decode kcp msg is nil, convId: %v", convId)
|
logger.Error("decode kcp msg is nil, convId: %v", convId)
|
||||||
continue
|
continue
|
||||||
|
|||||||
Reference in New Issue
Block a user