mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-04 15:52:27 +08:00
实现网关服务器不同版本客户端协议代理功能
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -23,6 +23,8 @@ bin
|
|||||||
|
|
||||||
# Game protocol protobuf generate file
|
# Game protocol protobuf generate file
|
||||||
protocol/proto/*.pb.go
|
protocol/proto/*.pb.go
|
||||||
|
gate/client_proto/proto
|
||||||
|
gate/client_proto/client_proto_gen.go
|
||||||
|
|
||||||
# Log file
|
# Log file
|
||||||
*.log
|
*.log
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
[hk4e]
|
[hk4e]
|
||||||
kcp_addr = "127.0.0.1"
|
kcp_addr = "127.0.0.1"
|
||||||
kcp_port = 22103
|
kcp_port = 22103
|
||||||
|
client_proto_proxy_enable = false
|
||||||
|
|
||||||
[logger]
|
[logger]
|
||||||
level = "DEBUG"
|
level = "DEBUG"
|
||||||
|
|||||||
@@ -32,11 +32,12 @@ type Database struct {
|
|||||||
|
|
||||||
// Hk4e 原神相关
|
// Hk4e 原神相关
|
||||||
type Hk4e struct {
|
type Hk4e struct {
|
||||||
KcpPort int32 `toml:"kcp_port"`
|
KcpPort int32 `toml:"kcp_port"`
|
||||||
KcpAddr string `toml:"kcp_addr"`
|
KcpAddr string `toml:"kcp_addr"`
|
||||||
ResourcePath string `toml:"resource_path"`
|
ResourcePath string `toml:"resource_path"`
|
||||||
GameDataConfigPath string `toml:"game_data_config_path"`
|
GameDataConfigPath string `toml:"game_data_config_path"`
|
||||||
GachaHistoryServer string `toml:"gacha_history_server"`
|
GachaHistoryServer string `toml:"gacha_history_server"`
|
||||||
|
ClientProtoProxyEnable bool `toml:"client_proto_proxy_enable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// MQ 消息队列
|
// MQ 消息队列
|
||||||
|
|||||||
58
gate/client_proto/client_proto.go
Normal file
58
gate/client_proto/client_proto.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package client_proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"hk4e/pkg/logger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientCmdProtoMap struct {
|
||||||
|
clientCmdIdCmdNameMap map[uint16]string
|
||||||
|
clientCmdNameCmdIdMap map[string]uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewClientCmdProtoMap() (r *ClientCmdProtoMap) {
|
||||||
|
r = new(ClientCmdProtoMap)
|
||||||
|
r.clientCmdIdCmdNameMap = make(map[uint16]string)
|
||||||
|
r.clientCmdNameCmdIdMap = make(map[string]uint16)
|
||||||
|
clientCmdFile, err := os.ReadFile("./client_cmd.csv")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
clientCmdData := string(clientCmdFile)
|
||||||
|
lineList := strings.Split(clientCmdData, "\n")
|
||||||
|
for _, line := range lineList {
|
||||||
|
item := strings.Split(line, ",")
|
||||||
|
if len(item) != 2 {
|
||||||
|
panic("parse client cmd file error")
|
||||||
|
}
|
||||||
|
cmdName := item[0]
|
||||||
|
cmdId, err := strconv.Atoi(item[1])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
r.clientCmdIdCmdNameMap[uint16(cmdId)] = cmdName
|
||||||
|
r.clientCmdNameCmdIdMap[cmdName] = uint16(cmdId)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientCmdProtoMap) GetClientCmdNameByCmdId(cmdId uint16) string {
|
||||||
|
cmdName, exist := c.clientCmdIdCmdNameMap[cmdId]
|
||||||
|
if !exist {
|
||||||
|
logger.Error("unknown cmd id: %v", cmdId)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return cmdName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientCmdProtoMap) GetClientCmdIdByCmdName(cmdName string) uint16 {
|
||||||
|
cmdId, exist := c.clientCmdNameCmdIdMap[cmdName]
|
||||||
|
if !exist {
|
||||||
|
logger.Error("unknown cmd name: %v", cmdName)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return cmdId
|
||||||
|
}
|
||||||
33
gate/client_proto/client_proto_gen_test.go
Normal file
33
gate/client_proto/client_proto_gen_test.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package client_proto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClientProtoGen(t *testing.T) {
|
||||||
|
clientCmdProtoMap := NewClientCmdProtoMap()
|
||||||
|
|
||||||
|
fileData := "package client_proto\n"
|
||||||
|
fileData += "\n"
|
||||||
|
fileData += "import (\n"
|
||||||
|
fileData += "\"hk4e/gate/client_proto/proto\"\n"
|
||||||
|
fileData += "pb \"google.golang.org/protobuf/proto\"\n"
|
||||||
|
fileData += ")\n"
|
||||||
|
fileData += "\n"
|
||||||
|
fileData += "func (c *ClientCmdProtoMap) GetClientProtoObjByCmdName(cmdName string) pb.Message {\n"
|
||||||
|
fileData += "switch cmdName {\n"
|
||||||
|
for cmdName := range clientCmdProtoMap.clientCmdNameCmdIdMap {
|
||||||
|
fileData += "case \"" + cmdName + "\":\nreturn new(proto." + cmdName + ")\n"
|
||||||
|
}
|
||||||
|
fileData += "default:\n"
|
||||||
|
fileData += "return nil\n"
|
||||||
|
fileData += "}\n"
|
||||||
|
fileData += "}\n"
|
||||||
|
fileData += "\n"
|
||||||
|
|
||||||
|
err := os.WriteFile("../client_proto_gen.go", []byte(fileData), 0644)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -12,6 +13,7 @@ import (
|
|||||||
"hk4e/common/mq"
|
"hk4e/common/mq"
|
||||||
"hk4e/common/region"
|
"hk4e/common/region"
|
||||||
"hk4e/common/rpc"
|
"hk4e/common/rpc"
|
||||||
|
"hk4e/gate/client_proto"
|
||||||
"hk4e/gate/kcp"
|
"hk4e/gate/kcp"
|
||||||
"hk4e/node/api"
|
"hk4e/node/api"
|
||||||
"hk4e/pkg/logger"
|
"hk4e/pkg/logger"
|
||||||
@@ -22,18 +24,20 @@ import (
|
|||||||
const PacketFreqLimit = 1000
|
const PacketFreqLimit = 1000
|
||||||
|
|
||||||
type KcpConnectManager struct {
|
type KcpConnectManager struct {
|
||||||
discovery *rpc.DiscoveryClient
|
discovery *rpc.DiscoveryClient
|
||||||
openState bool
|
openState bool
|
||||||
sessionConvIdMap map[uint64]*Session
|
sessionConvIdMap map[uint64]*Session
|
||||||
sessionUserIdMap map[uint32]*Session
|
sessionUserIdMap map[uint32]*Session
|
||||||
sessionMapLock sync.RWMutex
|
sessionMapLock sync.RWMutex
|
||||||
kcpEventInput chan *KcpEvent
|
kcpEventInput chan *KcpEvent
|
||||||
kcpEventOutput chan *KcpEvent
|
kcpEventOutput chan *KcpEvent
|
||||||
cmdProtoMap *cmd.CmdProtoMap
|
serverCmdProtoMap *cmd.CmdProtoMap
|
||||||
messageQueue *mq.MessageQueue
|
clientCmdProtoMap *client_proto.ClientCmdProtoMap
|
||||||
localMsgOutput chan *ProtoMsg
|
clientCmdProtoMapRefValue reflect.Value
|
||||||
createSessionChan chan *Session
|
messageQueue *mq.MessageQueue
|
||||||
destroySessionChan chan *Session
|
localMsgOutput chan *ProtoMsg
|
||||||
|
createSessionChan chan *Session
|
||||||
|
destroySessionChan chan *Session
|
||||||
// 密钥相关
|
// 密钥相关
|
||||||
dispatchKey []byte
|
dispatchKey []byte
|
||||||
signRsaKey []byte
|
signRsaKey []byte
|
||||||
@@ -48,7 +52,9 @@ func NewKcpConnectManager(messageQueue *mq.MessageQueue, discovery *rpc.Discover
|
|||||||
r.sessionUserIdMap = make(map[uint32]*Session)
|
r.sessionUserIdMap = make(map[uint32]*Session)
|
||||||
r.kcpEventInput = make(chan *KcpEvent, 1000)
|
r.kcpEventInput = make(chan *KcpEvent, 1000)
|
||||||
r.kcpEventOutput = make(chan *KcpEvent, 1000)
|
r.kcpEventOutput = make(chan *KcpEvent, 1000)
|
||||||
r.cmdProtoMap = cmd.NewCmdProtoMap()
|
r.serverCmdProtoMap = cmd.NewCmdProtoMap()
|
||||||
|
r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
||||||
|
r.clientCmdProtoMapRefValue = reflect.ValueOf(r.clientCmdProtoMap)
|
||||||
r.messageQueue = messageQueue
|
r.messageQueue = messageQueue
|
||||||
r.localMsgOutput = make(chan *ProtoMsg, 1000)
|
r.localMsgOutput = make(chan *ProtoMsg, 1000)
|
||||||
r.createSessionChan = make(chan *Session, 1000)
|
r.createSessionChan = make(chan *Session, 1000)
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package net
|
package net
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"hk4e/common/config"
|
||||||
"hk4e/pkg/logger"
|
"hk4e/pkg/logger"
|
||||||
|
"hk4e/pkg/object"
|
||||||
"hk4e/protocol/cmd"
|
"hk4e/protocol/cmd"
|
||||||
"hk4e/protocol/proto"
|
"hk4e/protocol/proto"
|
||||||
|
|
||||||
@@ -22,6 +26,33 @@ type ProtoMessage struct {
|
|||||||
|
|
||||||
func (k *KcpConnectManager) protoDecode(kcpMsg *KcpMsg) (protoMsgList []*ProtoMsg) {
|
func (k *KcpConnectManager) protoDecode(kcpMsg *KcpMsg) (protoMsgList []*ProtoMsg) {
|
||||||
protoMsgList = make([]*ProtoMsg, 0)
|
protoMsgList = make([]*ProtoMsg, 0)
|
||||||
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
|
clientCmdId := kcpMsg.CmdId
|
||||||
|
clientProtoData := kcpMsg.ProtoData
|
||||||
|
cmdName := k.clientCmdProtoMap.GetClientCmdNameByCmdId(clientCmdId)
|
||||||
|
clientProtoObj := k.clientCmdProtoMapRefValue.MethodByName(
|
||||||
|
"GetClientProtoObjByCmdName",
|
||||||
|
).Call([]reflect.Value{reflect.ValueOf(cmdName)})[0].Interface().(pb.Message)
|
||||||
|
err := pb.Unmarshal(clientProtoData, clientProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("unmarshal client proto error: %v", err)
|
||||||
|
return protoMsgList
|
||||||
|
}
|
||||||
|
serverCmdId := k.serverCmdProtoMap.GetCmdIdByCmdName(cmdName)
|
||||||
|
serverProtoObj := k.serverCmdProtoMap.GetProtoObjByCmdId(serverCmdId)
|
||||||
|
err = object.CopyProtoBufSameField(serverProtoObj, clientProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("copy proto obj error: %v", err)
|
||||||
|
return protoMsgList
|
||||||
|
}
|
||||||
|
serverProtoData, err := pb.Marshal(serverProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("marshal server proto error: %v", err)
|
||||||
|
return protoMsgList
|
||||||
|
}
|
||||||
|
kcpMsg.CmdId = serverCmdId
|
||||||
|
kcpMsg.ProtoData = serverProtoData
|
||||||
|
}
|
||||||
protoMsg := new(ProtoMsg)
|
protoMsg := new(ProtoMsg)
|
||||||
protoMsg.ConvId = kcpMsg.ConvId
|
protoMsg.ConvId = kcpMsg.ConvId
|
||||||
protoMsg.CmdId = kcpMsg.CmdId
|
protoMsg.CmdId = kcpMsg.CmdId
|
||||||
@@ -130,11 +161,36 @@ func (k *KcpConnectManager) protoEncode(protoMsg *ProtoMsg) (kcpMsg *KcpMsg) {
|
|||||||
} else {
|
} else {
|
||||||
kcpMsg.ProtoData = nil
|
kcpMsg.ProtoData = nil
|
||||||
}
|
}
|
||||||
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
|
serverCmdId := kcpMsg.CmdId
|
||||||
|
serverProtoData := kcpMsg.ProtoData
|
||||||
|
serverProtoObj := k.serverCmdProtoMap.GetProtoObjByCmdId(serverCmdId)
|
||||||
|
err := pb.Unmarshal(serverProtoData, serverProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("unmarshal server proto error: %v", err)
|
||||||
|
}
|
||||||
|
cmdName := k.serverCmdProtoMap.GetCmdNameByCmdId(serverCmdId)
|
||||||
|
clientProtoObj := k.clientCmdProtoMapRefValue.MethodByName(
|
||||||
|
"GetClientProtoObjByCmdName",
|
||||||
|
).Call([]reflect.Value{reflect.ValueOf(cmdName)})[0].Interface().(pb.Message)
|
||||||
|
err = object.CopyProtoBufSameField(clientProtoObj, serverProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("copy proto obj error: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
clientProtoData, err := pb.Marshal(clientProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("marshal client proto error: %v", err)
|
||||||
|
}
|
||||||
|
clientCmdId := k.clientCmdProtoMap.GetClientCmdIdByCmdName(cmdName)
|
||||||
|
kcpMsg.CmdId = clientCmdId
|
||||||
|
kcpMsg.ProtoData = clientProtoData
|
||||||
|
}
|
||||||
return kcpMsg
|
return kcpMsg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *KcpConnectManager) decodePayloadToProto(cmdId uint16, protoData []byte) (protoObj pb.Message) {
|
func (k *KcpConnectManager) decodePayloadToProto(cmdId uint16, protoData []byte) (protoObj pb.Message) {
|
||||||
protoObj = k.cmdProtoMap.GetProtoObjByCmdId(cmdId)
|
protoObj = k.serverCmdProtoMap.GetProtoObjByCmdId(cmdId)
|
||||||
if protoObj == nil {
|
if protoObj == nil {
|
||||||
logger.Error("get new proto object is nil")
|
logger.Error("get new proto object is nil")
|
||||||
return nil
|
return nil
|
||||||
@@ -148,7 +204,7 @@ func (k *KcpConnectManager) decodePayloadToProto(cmdId uint16, protoData []byte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (k *KcpConnectManager) encodeProtoToPayload(protoObj pb.Message) (cmdId uint16, protoData []byte) {
|
func (k *KcpConnectManager) encodeProtoToPayload(protoObj pb.Message) (cmdId uint16, protoData []byte) {
|
||||||
cmdId = k.cmdProtoMap.GetCmdIdByProtoObj(protoObj)
|
cmdId = k.serverCmdProtoMap.GetCmdIdByProtoObj(protoObj)
|
||||||
var err error = nil
|
var err error = nil
|
||||||
protoData, err = pb.Marshal(protoObj)
|
protoData, err = pb.Marshal(protoObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -3,12 +3,16 @@ package object
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/vmihailenco/msgpack/v5"
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
|
pb "google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DeepCopy(dst, src any) error {
|
func FullDeepCopy(dst, src any) error {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
err := gob.NewEncoder(&buf).Encode(src)
|
err := gob.NewEncoder(&buf).Encode(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,6 +37,52 @@ func FastDeepCopy(dst, src any) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CopyProtoBufSameField(dst, src pb.Message) error {
|
||||||
|
date, err := protojson.Marshal(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
jsonObj := make(map[string]any)
|
||||||
|
err = json.Unmarshal(date, &jsonObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
jsonData, err := json.Marshal(jsonObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = protojson.Unmarshal(jsonData, dst)
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "unknown field") {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
split := strings.Split(err.Error(), "\"")
|
||||||
|
if len(split) != 3 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fieldName := split[1]
|
||||||
|
DeleteAllKeyNameFromStringAnyMap(jsonObj, fieldName)
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteAllKeyNameFromStringAnyMap(src map[string]any, keyName string) {
|
||||||
|
for key, value := range src {
|
||||||
|
v, ok := value.(map[string]any)
|
||||||
|
if ok {
|
||||||
|
DeleteAllKeyNameFromStringAnyMap(v, keyName)
|
||||||
|
}
|
||||||
|
if key == keyName {
|
||||||
|
delete(src, key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ConvBoolToInt64(v bool) int64 {
|
func ConvBoolToInt64(v bool) int64 {
|
||||||
if v {
|
if v {
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ func ConvStructToMap(value any) map[string]any {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
fieldNum := refType.NumField()
|
fieldNum := refType.NumField()
|
||||||
result := make(map[string]any)
|
|
||||||
nameList := make([]string, 0)
|
nameList := make([]string, 0)
|
||||||
for i := 0; i < fieldNum; i++ {
|
for i := 0; i < fieldNum; i++ {
|
||||||
nameList = append(nameList, refType.Field(i).Name)
|
nameList = append(nameList, refType.Field(i).Name)
|
||||||
@@ -20,6 +19,7 @@ func ConvStructToMap(value any) map[string]any {
|
|||||||
if refValue.Kind() == reflect.Ptr {
|
if refValue.Kind() == reflect.Ptr {
|
||||||
refValue = refValue.Elem()
|
refValue = refValue.Elem()
|
||||||
}
|
}
|
||||||
|
result := make(map[string]any)
|
||||||
for i := 0; i < fieldNum; i++ {
|
for i := 0; i < fieldNum; i++ {
|
||||||
result[nameList[i]] = refValue.FieldByName(nameList[i]).Interface()
|
result[nameList[i]] = refValue.FieldByName(nameList[i]).Interface()
|
||||||
}
|
}
|
||||||
@@ -77,3 +77,59 @@ func CopyStructField(dst any, src any, fieldName string) bool {
|
|||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CopyStructSameField(dst any, src any) bool {
|
||||||
|
// dst
|
||||||
|
dstRefType := reflect.TypeOf(dst)
|
||||||
|
if dstRefType.Kind() != reflect.Ptr {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
dstRefType = dstRefType.Elem()
|
||||||
|
if dstRefType.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
dstRefValue := reflect.ValueOf(dst)
|
||||||
|
if dstRefValue.Kind() != reflect.Ptr {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
dstRefValue = dstRefValue.Elem()
|
||||||
|
// src
|
||||||
|
srcRefType := reflect.TypeOf(src)
|
||||||
|
if srcRefType.Kind() != reflect.Ptr {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
srcRefType = srcRefType.Elem()
|
||||||
|
if srcRefType.Kind() != reflect.Struct {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
srcRefValue := reflect.ValueOf(src)
|
||||||
|
if srcRefValue.Kind() != reflect.Ptr {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
srcRefValue = srcRefValue.Elem()
|
||||||
|
// copy
|
||||||
|
fieldNum := srcRefType.NumField()
|
||||||
|
for i := 0; i < fieldNum; i++ {
|
||||||
|
srcFieldType := srcRefType.Field(i)
|
||||||
|
if !srcFieldType.IsExported() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldName := srcFieldType.Name
|
||||||
|
dstFieldType, ok := dstRefType.FieldByName(fieldName)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
srcField := srcRefValue.FieldByName(fieldName)
|
||||||
|
dstField := dstRefValue.FieldByName(fieldName)
|
||||||
|
if srcField.Kind() == reflect.Ptr {
|
||||||
|
dstField.Set(reflect.New(dstFieldType.Type.Elem()))
|
||||||
|
CopyStructSameField(dstField.Interface(), srcField.Interface())
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if dstField.Type() != reflect.TypeOf(srcField.Interface()) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
dstField.Set(reflect.ValueOf(srcField.Interface()))
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
49
pkg/reflection/struct_test.go
Normal file
49
pkg/reflection/struct_test.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package reflection
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type XXX struct {
|
||||||
|
Time int64
|
||||||
|
Date string
|
||||||
|
}
|
||||||
|
|
||||||
|
type YYY struct {
|
||||||
|
Ping uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type AAA struct {
|
||||||
|
Name string
|
||||||
|
UserId uint32
|
||||||
|
A uint8
|
||||||
|
X *XXX
|
||||||
|
Y YYY
|
||||||
|
}
|
||||||
|
|
||||||
|
type BBB struct {
|
||||||
|
Name string
|
||||||
|
UserId uint32
|
||||||
|
B uint8
|
||||||
|
X *XXX
|
||||||
|
Y YYY
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCopyStructSameField(t *testing.T) {
|
||||||
|
aaa := &AAA{
|
||||||
|
Name: "flswld",
|
||||||
|
UserId: 100000001,
|
||||||
|
A: 111,
|
||||||
|
X: &XXX{
|
||||||
|
Time: 150405,
|
||||||
|
Date: "2006-01-02",
|
||||||
|
},
|
||||||
|
Y: YYY{
|
||||||
|
Ping: 999,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
bbb := new(BBB)
|
||||||
|
ok := CopyStructSameField(bbb, aaa)
|
||||||
|
fmt.Println(ok)
|
||||||
|
}
|
||||||
@@ -13,6 +13,8 @@ type CmdProtoMap struct {
|
|||||||
cmdIdProtoObjMap map[uint16]reflect.Type
|
cmdIdProtoObjMap map[uint16]reflect.Type
|
||||||
protoObjCmdIdMap map[reflect.Type]uint16
|
protoObjCmdIdMap map[reflect.Type]uint16
|
||||||
cmdDeDupMap map[uint16]bool
|
cmdDeDupMap map[uint16]bool
|
||||||
|
cmdIdCmdNameMap map[uint16]string
|
||||||
|
cmdNameCmdIdMap map[string]uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewCmdProtoMap() (r *CmdProtoMap) {
|
func NewCmdProtoMap() (r *CmdProtoMap) {
|
||||||
@@ -20,6 +22,8 @@ func NewCmdProtoMap() (r *CmdProtoMap) {
|
|||||||
r.cmdIdProtoObjMap = make(map[uint16]reflect.Type)
|
r.cmdIdProtoObjMap = make(map[uint16]reflect.Type)
|
||||||
r.protoObjCmdIdMap = make(map[reflect.Type]uint16)
|
r.protoObjCmdIdMap = make(map[reflect.Type]uint16)
|
||||||
r.cmdDeDupMap = make(map[uint16]bool)
|
r.cmdDeDupMap = make(map[uint16]bool)
|
||||||
|
r.cmdIdCmdNameMap = make(map[uint16]string)
|
||||||
|
r.cmdNameCmdIdMap = make(map[string]uint16)
|
||||||
r.registerAllMessage()
|
r.registerAllMessage()
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -268,30 +272,52 @@ func (c *CmdProtoMap) registerMessage(cmdId uint16, protoObj pb.Message) {
|
|||||||
} else {
|
} else {
|
||||||
c.cmdDeDupMap[cmdId] = true
|
c.cmdDeDupMap[cmdId] = true
|
||||||
}
|
}
|
||||||
|
refType := reflect.TypeOf(protoObj)
|
||||||
// cmdId -> protoObj
|
// cmdId -> protoObj
|
||||||
c.cmdIdProtoObjMap[cmdId] = reflect.TypeOf(protoObj)
|
c.cmdIdProtoObjMap[cmdId] = refType
|
||||||
// protoObj -> cmdId
|
// protoObj -> cmdId
|
||||||
c.protoObjCmdIdMap[reflect.TypeOf(protoObj)] = cmdId
|
c.protoObjCmdIdMap[refType] = cmdId
|
||||||
|
cmdName := refType.Elem().Name()
|
||||||
|
// cmdId -> cmdName
|
||||||
|
c.cmdIdCmdNameMap[cmdId] = cmdName
|
||||||
|
// cmdName -> cmdId
|
||||||
|
c.cmdNameCmdIdMap[cmdName] = cmdId
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CmdProtoMap) GetProtoObjByCmdId(cmdId uint16) (protoObj pb.Message) {
|
func (c *CmdProtoMap) GetProtoObjByCmdId(cmdId uint16) pb.Message {
|
||||||
protoObjTypePointer, ok := c.cmdIdProtoObjMap[cmdId]
|
refType, exist := c.cmdIdProtoObjMap[cmdId]
|
||||||
if !ok {
|
if !exist {
|
||||||
logger.Error("unknown cmd id: %v", cmdId)
|
logger.Error("unknown cmd id: %v", cmdId)
|
||||||
protoObj = nil
|
return nil
|
||||||
return protoObj
|
|
||||||
}
|
}
|
||||||
protoObjInst := reflect.New(protoObjTypePointer.Elem())
|
protoObjInst := reflect.New(refType.Elem())
|
||||||
protoObj = protoObjInst.Interface().(pb.Message)
|
protoObj := protoObjInst.Interface().(pb.Message)
|
||||||
return protoObj
|
return protoObj
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CmdProtoMap) GetCmdIdByProtoObj(protoObj pb.Message) (cmdId uint16) {
|
func (c *CmdProtoMap) GetCmdIdByProtoObj(protoObj pb.Message) uint16 {
|
||||||
var ok = false
|
cmdId, exist := c.protoObjCmdIdMap[reflect.TypeOf(protoObj)]
|
||||||
cmdId, ok = c.protoObjCmdIdMap[reflect.TypeOf(protoObj)]
|
if !exist {
|
||||||
if !ok {
|
|
||||||
logger.Error("unknown proto object: %v", protoObj)
|
logger.Error("unknown proto object: %v", protoObj)
|
||||||
cmdId = 0
|
return 0
|
||||||
|
}
|
||||||
|
return cmdId
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdProtoMap) GetCmdNameByCmdId(cmdId uint16) string {
|
||||||
|
cmdName, exist := c.cmdIdCmdNameMap[cmdId]
|
||||||
|
if !exist {
|
||||||
|
logger.Error("unknown cmd id: %v", cmdId)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return cmdName
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CmdProtoMap) GetCmdIdByCmdName(cmdName string) uint16 {
|
||||||
|
cmdId, exist := c.cmdNameCmdIdMap[cmdName]
|
||||||
|
if !exist {
|
||||||
|
logger.Error("unknown cmd name: %v", cmdName)
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
return cmdId
|
return cmdId
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user