mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-02-04 16:02:26 +08:00
客户端协议代理功能
This commit is contained in:
14
README.md
14
README.md
@@ -14,10 +14,10 @@ hk4e game server
|
|||||||
* mongodb
|
* mongodb
|
||||||
* nats-server
|
* nats-server
|
||||||
|
|
||||||
1. 启动节点服务器 `cmd/node && go run .`
|
1. 启动节点服务器(仅单节点) `cmd/node && go run .`
|
||||||
2. 启动http登录服务器 `cmd/dispatch && go run .`
|
2. 启动http登录服务器(可多节点) `cmd/dispatch && go run .`
|
||||||
3. 启动网关服务器 `cd cmd/gate && go run .`
|
3. 启动网关服务器(可多节点) `cd cmd/gate && go run .`
|
||||||
4. 启动战斗服务器 `cmd/fight && go run .`
|
4. 启动战斗服务器(可多节点) `cmd/fight && go run .`
|
||||||
5. 启动寻路服务器 `cmd/pathfinding && go run .`
|
5. 启动寻路服务器(可多节点) `cmd/pathfinding && go run .`
|
||||||
6. 启动游戏服务器 `cd cmd/gs && go run .`
|
6. 启动游戏服务器(可多节点) `cd cmd/gs && go run .`
|
||||||
7. 启动游戏管理服务器 `cmd/gm && go run .`
|
7. 启动游戏管理服务器(仅单节点) `cmd/gm && go run .`
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
[hk4e]
|
||||||
|
client_proto_proxy_enable = false
|
||||||
|
|
||||||
[logger]
|
[logger]
|
||||||
level = "DEBUG"
|
level = "DEBUG"
|
||||||
mode = "BOTH"
|
mode = "BOTH"
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
[hk4e]
|
[hk4e]
|
||||||
|
client_proto_proxy_enable = false
|
||||||
resource_path = "./GameDataConfigTable"
|
resource_path = "./GameDataConfigTable"
|
||||||
game_data_config_path = "./game_data_config"
|
game_data_config_path = "./game_data_config"
|
||||||
gacha_history_server = "https://hk4e.flswld.com/api/v1"
|
gacha_history_server = "https://hk4e.flswld.com/api/v1"
|
||||||
|
|||||||
34
common/utils/utils.go
Normal file
34
common/utils/utils.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"hk4e/common/config"
|
||||||
|
"hk4e/pkg/logger"
|
||||||
|
"hk4e/pkg/object"
|
||||||
|
|
||||||
|
pb "google.golang.org/protobuf/proto"
|
||||||
|
)
|
||||||
|
|
||||||
|
func UnmarshalProtoObj(serverProtoObj pb.Message, clientProtoObj pb.Message, data []byte) bool {
|
||||||
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
|
err := pb.Unmarshal(data, clientProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("parse client proto obj error: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
delList, err := object.CopyProtoBufSameField(serverProtoObj, clientProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("copy proto obj error: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(delList) != 0 {
|
||||||
|
logger.Error("delete field name list: %v", delList)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := pb.Unmarshal(data, serverProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("parse server proto obj error: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
package engine
|
package engine
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"hk4e/common/config"
|
||||||
"hk4e/common/constant"
|
"hk4e/common/constant"
|
||||||
"hk4e/common/mq"
|
"hk4e/common/mq"
|
||||||
|
"hk4e/common/utils"
|
||||||
|
"hk4e/gate/client_proto"
|
||||||
"hk4e/pkg/logger"
|
"hk4e/pkg/logger"
|
||||||
"hk4e/protocol/cmd"
|
"hk4e/protocol/cmd"
|
||||||
"hk4e/protocol/proto"
|
"hk4e/protocol/proto"
|
||||||
@@ -19,6 +23,7 @@ type FightEngine struct {
|
|||||||
func NewFightEngine(messageQueue *mq.MessageQueue) (r *FightEngine) {
|
func NewFightEngine(messageQueue *mq.MessageQueue) (r *FightEngine) {
|
||||||
r = new(FightEngine)
|
r = new(FightEngine)
|
||||||
r.messageQueue = messageQueue
|
r.messageQueue = messageQueue
|
||||||
|
initClientCmdProtoMap()
|
||||||
go r.fightHandle()
|
go r.fightHandle()
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@@ -223,9 +228,13 @@ func (f *FightRoutine) attackHandle(gameMsg *mq.GameMsg) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
hitInfo := new(proto.EvtBeingHitInfo)
|
hitInfo := new(proto.EvtBeingHitInfo)
|
||||||
err := pb.Unmarshal(entry.CombatData, hitInfo)
|
clientProtoObj := GetClientProtoObjByName("EvtBeingHitInfo")
|
||||||
if err != nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("parse combat invocations entity hit info error: %v", err)
|
logger.Error("get client proto obj is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok := utils.UnmarshalProtoObj(hitInfo, clientProtoObj, entry.CombatData)
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
attackResult := hitInfo.AttackResult
|
attackResult := hitInfo.AttackResult
|
||||||
@@ -278,3 +287,23 @@ func (f *FightRoutine) getAllPlayer(entityMap map[uint32]*Entity) []uint32 {
|
|||||||
}
|
}
|
||||||
return uidList
|
return uidList
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ClientCmdProtoMap *client_proto.ClientCmdProtoMap
|
||||||
|
var ClientCmdProtoMapRefValue reflect.Value
|
||||||
|
|
||||||
|
func initClientCmdProtoMap() {
|
||||||
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
|
ClientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
||||||
|
ClientCmdProtoMapRefValue = reflect.ValueOf(ClientCmdProtoMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetClientProtoObjByName(protoObjName string) pb.Message {
|
||||||
|
if !config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
|
return &proto.NullMsg{}
|
||||||
|
}
|
||||||
|
clientProtoObj := ClientCmdProtoMapRefValue.MethodByName(
|
||||||
|
"GetClientProtoObjByName",
|
||||||
|
).Call([]reflect.Value{reflect.ValueOf(protoObjName)})[0].Interface().(pb.Message)
|
||||||
|
return clientProtoObj
|
||||||
|
}
|
||||||
|
|||||||
16
gate/client_proto/README.md
Normal file
16
gate/client_proto/README.md
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# 客户端协议代理功能
|
||||||
|
|
||||||
|
## 功能介绍
|
||||||
|
|
||||||
|
### 开启本功能后,网关服务器以及游戏服务器等其他服务器,将预先对客户端上行和服务器下行的协议数据做前置转换,采用任意版本的协议文件(必要字段名必须与现有的协议保持一致)均可,避免了因协议序号混淆等频繁变动,而造成游戏服务器代码不必要的频繁改动
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
1. 在此目录下建立bin目录和proto目录
|
||||||
|
2. 将client_cmd.csv文件放到bin目录下
|
||||||
|
3. 将对应版本的proto协议文件复制到proto目录下并编译成pb.go
|
||||||
|
4. 将client_proto_gen_test.go的TestClientProtoGen方法添加运行配置
|
||||||
|
5. 将运行配置输出目录和工作目录都设置为bin目录
|
||||||
|
6. 运行并生成client_proto_gen.go
|
||||||
|
7. 将client_cmd.csv放入gate和gs和fight服务器的运行目录下
|
||||||
|
8. 将gate和gs和fight服务器的配置文件中开启client_proto_proxy_enable客户端协议代理功能
|
||||||
@@ -2,11 +2,23 @@ package client_proto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestClientProtoGen(t *testing.T) {
|
func TestClientProtoGen(t *testing.T) {
|
||||||
clientCmdProtoMap := NewClientCmdProtoMap()
|
dir, err := os.ReadDir("../proto")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
nameList := make([]string, 0)
|
||||||
|
for _, entry := range dir {
|
||||||
|
split := strings.Split(entry.Name(), ".")
|
||||||
|
if len(split) != 2 {
|
||||||
|
panic("file name error")
|
||||||
|
}
|
||||||
|
nameList = append(nameList, split[0])
|
||||||
|
}
|
||||||
|
|
||||||
fileData := "package client_proto\n"
|
fileData := "package client_proto\n"
|
||||||
fileData += "\n"
|
fileData += "\n"
|
||||||
@@ -15,10 +27,10 @@ func TestClientProtoGen(t *testing.T) {
|
|||||||
fileData += "pb \"google.golang.org/protobuf/proto\"\n"
|
fileData += "pb \"google.golang.org/protobuf/proto\"\n"
|
||||||
fileData += ")\n"
|
fileData += ")\n"
|
||||||
fileData += "\n"
|
fileData += "\n"
|
||||||
fileData += "func (c *ClientCmdProtoMap) GetClientProtoObjByCmdName(cmdName string) pb.Message {\n"
|
fileData += "func (c *ClientCmdProtoMap) GetClientProtoObjByName(protoObjName string) pb.Message {\n"
|
||||||
fileData += "switch cmdName {\n"
|
fileData += "switch protoObjName {\n"
|
||||||
for cmdName := range clientCmdProtoMap.clientCmdNameCmdIdMap {
|
for _, protoObjName := range nameList {
|
||||||
fileData += "case \"" + cmdName + "\":\nreturn new(proto." + cmdName + ")\n"
|
fileData += "case \"" + protoObjName + "\":\nreturn new(proto." + protoObjName + ")\n"
|
||||||
}
|
}
|
||||||
fileData += "default:\n"
|
fileData += "default:\n"
|
||||||
fileData += "return nil\n"
|
fileData += "return nil\n"
|
||||||
@@ -26,7 +38,7 @@ func TestClientProtoGen(t *testing.T) {
|
|||||||
fileData += "}\n"
|
fileData += "}\n"
|
||||||
fileData += "\n"
|
fileData += "\n"
|
||||||
|
|
||||||
err := os.WriteFile("../client_proto_gen.go", []byte(fileData), 0644)
|
err = os.WriteFile("../client_proto_gen.go", []byte(fileData), 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,8 +53,10 @@ func NewKcpConnectManager(messageQueue *mq.MessageQueue, discovery *rpc.Discover
|
|||||||
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.serverCmdProtoMap = cmd.NewCmdProtoMap()
|
r.serverCmdProtoMap = cmd.NewCmdProtoMap()
|
||||||
r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
r.clientCmdProtoMapRefValue = reflect.ValueOf(r.clientCmdProtoMap)
|
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)
|
||||||
|
|||||||
@@ -30,21 +30,38 @@ func (k *KcpConnectManager) protoDecode(kcpMsg *KcpMsg) (protoMsgList []*ProtoMs
|
|||||||
clientCmdId := kcpMsg.CmdId
|
clientCmdId := kcpMsg.CmdId
|
||||||
clientProtoData := kcpMsg.ProtoData
|
clientProtoData := kcpMsg.ProtoData
|
||||||
cmdName := k.clientCmdProtoMap.GetClientCmdNameByCmdId(clientCmdId)
|
cmdName := k.clientCmdProtoMap.GetClientCmdNameByCmdId(clientCmdId)
|
||||||
clientProtoObj := k.clientCmdProtoMapRefValue.MethodByName(
|
if cmdName == "" {
|
||||||
"GetClientProtoObjByCmdName",
|
logger.Error("get cmdName is nil, clientCmdId: %v", clientCmdId)
|
||||||
).Call([]reflect.Value{reflect.ValueOf(cmdName)})[0].Interface().(pb.Message)
|
return protoMsgList
|
||||||
|
}
|
||||||
|
clientProtoObj := k.getClientProtoObjByName(cmdName)
|
||||||
|
if clientProtoObj == nil {
|
||||||
|
logger.Error("get client proto obj is nil, cmdName: %v", cmdName)
|
||||||
|
return protoMsgList
|
||||||
|
}
|
||||||
err := pb.Unmarshal(clientProtoData, clientProtoObj)
|
err := pb.Unmarshal(clientProtoData, clientProtoObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("unmarshal client proto error: %v", err)
|
logger.Error("unmarshal client proto error: %v", err)
|
||||||
return protoMsgList
|
return protoMsgList
|
||||||
}
|
}
|
||||||
serverCmdId := k.serverCmdProtoMap.GetCmdIdByCmdName(cmdName)
|
serverCmdId := k.serverCmdProtoMap.GetCmdIdByCmdName(cmdName)
|
||||||
|
if serverCmdId == 0 {
|
||||||
|
logger.Error("get server cmdId is nil, cmdName: %v", cmdName)
|
||||||
|
return protoMsgList
|
||||||
|
}
|
||||||
serverProtoObj := k.serverCmdProtoMap.GetProtoObjByCmdId(serverCmdId)
|
serverProtoObj := k.serverCmdProtoMap.GetProtoObjByCmdId(serverCmdId)
|
||||||
err = object.CopyProtoBufSameField(serverProtoObj, clientProtoObj)
|
if serverProtoObj == nil {
|
||||||
|
logger.Error("get server proto obj is nil, serverCmdId: %v", serverCmdId)
|
||||||
|
return protoMsgList
|
||||||
|
}
|
||||||
|
delList, err := object.CopyProtoBufSameField(serverProtoObj, clientProtoObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("copy proto obj error: %v", err)
|
logger.Error("copy proto obj error: %v", err)
|
||||||
return protoMsgList
|
return protoMsgList
|
||||||
}
|
}
|
||||||
|
if len(delList) != 0 {
|
||||||
|
logger.Error("delete field name list: %v, cmdName: %v", delList, cmdName)
|
||||||
|
}
|
||||||
serverProtoData, err := pb.Marshal(serverProtoObj)
|
serverProtoData, err := pb.Marshal(serverProtoObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("marshal server proto error: %v", err)
|
logger.Error("marshal server proto error: %v", err)
|
||||||
@@ -117,6 +134,50 @@ func (k *KcpConnectManager) protoDecodePayloadLoop(cmdId uint16, protoData []byt
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, unionCmd := range unionCmdNotify.GetCmdList() {
|
for _, unionCmd := range unionCmdNotify.GetCmdList() {
|
||||||
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
|
clientCmdId := uint16(unionCmd.MessageId)
|
||||||
|
clientProtoData := unionCmd.Body
|
||||||
|
cmdName := k.clientCmdProtoMap.GetClientCmdNameByCmdId(clientCmdId)
|
||||||
|
if cmdName == "" {
|
||||||
|
logger.Error("get cmdName is nil, clientCmdId: %v", clientCmdId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
clientProtoObj := k.getClientProtoObjByName(cmdName)
|
||||||
|
if clientProtoObj == nil {
|
||||||
|
logger.Error("get client proto obj is nil, cmdName: %v", cmdName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err := pb.Unmarshal(clientProtoData, clientProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("unmarshal client proto error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
serverCmdId := k.serverCmdProtoMap.GetCmdIdByCmdName(cmdName)
|
||||||
|
if serverCmdId == 0 {
|
||||||
|
logger.Error("get server cmdId is nil, cmdName: %v", cmdName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
serverProtoObj := k.serverCmdProtoMap.GetProtoObjByCmdId(serverCmdId)
|
||||||
|
if serverProtoObj == nil {
|
||||||
|
logger.Error("get server proto obj is nil, serverCmdId: %v", serverCmdId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
delList, err := object.CopyProtoBufSameField(serverProtoObj, clientProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("copy proto obj error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if len(delList) != 0 {
|
||||||
|
logger.Error("delete field name list: %v, cmdName: %v", delList, cmdName)
|
||||||
|
}
|
||||||
|
serverProtoData, err := pb.Marshal(serverProtoObj)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("marshal server proto error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
unionCmd.MessageId = uint32(serverCmdId)
|
||||||
|
unionCmd.Body = serverProtoData
|
||||||
|
}
|
||||||
k.protoDecodePayloadLoop(uint16(unionCmd.MessageId), unionCmd.Body, protoMessageList)
|
k.protoDecodePayloadLoop(uint16(unionCmd.MessageId), unionCmd.Body, protoMessageList)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -165,24 +226,43 @@ func (k *KcpConnectManager) protoEncode(protoMsg *ProtoMsg) (kcpMsg *KcpMsg) {
|
|||||||
serverCmdId := kcpMsg.CmdId
|
serverCmdId := kcpMsg.CmdId
|
||||||
serverProtoData := kcpMsg.ProtoData
|
serverProtoData := kcpMsg.ProtoData
|
||||||
serverProtoObj := k.serverCmdProtoMap.GetProtoObjByCmdId(serverCmdId)
|
serverProtoObj := k.serverCmdProtoMap.GetProtoObjByCmdId(serverCmdId)
|
||||||
|
if serverProtoObj == nil {
|
||||||
|
logger.Error("get server proto obj is nil, serverCmdId: %v", serverCmdId)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
err := pb.Unmarshal(serverProtoData, serverProtoObj)
|
err := pb.Unmarshal(serverProtoData, serverProtoObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("unmarshal server proto error: %v", err)
|
logger.Error("unmarshal server proto error: %v", err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
cmdName := k.serverCmdProtoMap.GetCmdNameByCmdId(serverCmdId)
|
cmdName := k.serverCmdProtoMap.GetCmdNameByCmdId(serverCmdId)
|
||||||
clientProtoObj := k.clientCmdProtoMapRefValue.MethodByName(
|
if cmdName == "" {
|
||||||
"GetClientProtoObjByCmdName",
|
logger.Error("get cmdName is nil, serverCmdId: %v", serverCmdId)
|
||||||
).Call([]reflect.Value{reflect.ValueOf(cmdName)})[0].Interface().(pb.Message)
|
return nil
|
||||||
err = object.CopyProtoBufSameField(clientProtoObj, serverProtoObj)
|
}
|
||||||
|
clientProtoObj := k.getClientProtoObjByName(cmdName)
|
||||||
|
if clientProtoObj == nil {
|
||||||
|
logger.Error("get client proto obj is nil, cmdName: %v", cmdName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
delList, err := object.CopyProtoBufSameField(clientProtoObj, serverProtoObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("copy proto obj error: %v", err)
|
logger.Error("copy proto obj error: %v", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if len(delList) != 0 {
|
||||||
|
logger.Error("delete field name list: %v, cmdName: %v", delList, cmdName)
|
||||||
|
}
|
||||||
clientProtoData, err := pb.Marshal(clientProtoObj)
|
clientProtoData, err := pb.Marshal(clientProtoObj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("marshal client proto error: %v", err)
|
logger.Error("marshal client proto error: %v", err)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
clientCmdId := k.clientCmdProtoMap.GetClientCmdIdByCmdName(cmdName)
|
clientCmdId := k.clientCmdProtoMap.GetClientCmdIdByCmdName(cmdName)
|
||||||
|
if clientCmdId == 0 {
|
||||||
|
logger.Error("get client cmdId is nil, cmdName: %v", cmdName)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
kcpMsg.CmdId = clientCmdId
|
kcpMsg.CmdId = clientCmdId
|
||||||
kcpMsg.ProtoData = clientProtoData
|
kcpMsg.ProtoData = clientProtoData
|
||||||
}
|
}
|
||||||
@@ -213,3 +293,10 @@ func (k *KcpConnectManager) encodeProtoToPayload(protoObj pb.Message) (cmdId uin
|
|||||||
}
|
}
|
||||||
return cmdId, protoData
|
return cmdId, protoData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *KcpConnectManager) getClientProtoObjByName(protoObjName string) pb.Message {
|
||||||
|
clientProtoObj := k.clientCmdProtoMapRefValue.MethodByName(
|
||||||
|
"GetClientProtoObjByName",
|
||||||
|
).Call([]reflect.Value{reflect.ValueOf(protoObjName)})[0].Interface().(pb.Message)
|
||||||
|
return clientProtoObj
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
appConfig "hk4e/common/config"
|
||||||
"hk4e/common/mq"
|
"hk4e/common/mq"
|
||||||
|
"hk4e/gate/client_proto"
|
||||||
"hk4e/gate/kcp"
|
"hk4e/gate/kcp"
|
||||||
"hk4e/gs/dao"
|
"hk4e/gs/dao"
|
||||||
"hk4e/gs/model"
|
"hk4e/gs/model"
|
||||||
@@ -25,9 +28,11 @@ var TICK_MANAGER *TickManager = nil
|
|||||||
var COMMAND_MANAGER *CommandManager = nil
|
var COMMAND_MANAGER *CommandManager = nil
|
||||||
|
|
||||||
type GameManager struct {
|
type GameManager struct {
|
||||||
dao *dao.Dao
|
dao *dao.Dao
|
||||||
messageQueue *mq.MessageQueue
|
messageQueue *mq.MessageQueue
|
||||||
snowflake *alg.SnowflakeWorker
|
snowflake *alg.SnowflakeWorker
|
||||||
|
clientCmdProtoMap *client_proto.ClientCmdProtoMap
|
||||||
|
clientCmdProtoMapRefValue reflect.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32) (r *GameManager) {
|
func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32) (r *GameManager) {
|
||||||
@@ -35,6 +40,10 @@ func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32) (r
|
|||||||
r.dao = dao
|
r.dao = dao
|
||||||
r.messageQueue = messageQueue
|
r.messageQueue = messageQueue
|
||||||
r.snowflake = alg.NewSnowflakeWorker(int64(gsId))
|
r.snowflake = alg.NewSnowflakeWorker(int64(gsId))
|
||||||
|
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
|
r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
||||||
|
r.clientCmdProtoMapRefValue = reflect.ValueOf(r.clientCmdProtoMap)
|
||||||
|
}
|
||||||
GAME_MANAGER = r
|
GAME_MANAGER = r
|
||||||
LOCAL_EVENT_MANAGER = NewLocalEventManager()
|
LOCAL_EVENT_MANAGER = NewLocalEventManager()
|
||||||
ROUTE_MANAGER = NewRouteManager()
|
ROUTE_MANAGER = NewRouteManager()
|
||||||
@@ -266,3 +275,13 @@ func (g *GameManager) DisconnectPlayer(userId uint32, reason uint32) {
|
|||||||
})
|
})
|
||||||
// g.SendMsg(cmd.ServerDisconnectClientNotify, userId, 0, new(proto.ServerDisconnectClientNotify))
|
// g.SendMsg(cmd.ServerDisconnectClientNotify, userId, 0, new(proto.ServerDisconnectClientNotify))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *GameManager) GetClientProtoObjByName(protoObjName string) pb.Message {
|
||||||
|
if !appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
|
return &proto.NullMsg{}
|
||||||
|
}
|
||||||
|
clientProtoObj := g.clientCmdProtoMapRefValue.MethodByName(
|
||||||
|
"GetClientProtoObjByName",
|
||||||
|
).Call([]reflect.Value{reflect.ValueOf(protoObjName)})[0].Interface().(pb.Message)
|
||||||
|
return clientProtoObj
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"hk4e/common/utils"
|
||||||
"hk4e/gs/model"
|
"hk4e/gs/model"
|
||||||
"hk4e/pkg/logger"
|
"hk4e/pkg/logger"
|
||||||
"hk4e/pkg/reflection"
|
"hk4e/pkg/reflection"
|
||||||
@@ -103,9 +104,13 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
|
|||||||
continue
|
continue
|
||||||
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_ENTITY_MOVE:
|
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_ENTITY_MOVE:
|
||||||
entityMoveInfo := new(proto.EntityMoveInfo)
|
entityMoveInfo := new(proto.EntityMoveInfo)
|
||||||
err := pb.Unmarshal(entry.CombatData, entityMoveInfo)
|
clientProtoObj := g.GetClientProtoObjByName("EntityMoveInfo")
|
||||||
if err != nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("parse EntityMoveInfo error: %v", err)
|
logger.Error("get client proto obj is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok := utils.UnmarshalProtoObj(entityMoveInfo, clientProtoObj, entry.CombatData)
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
motionInfo := entityMoveInfo.MotionInfo
|
motionInfo := entityMoveInfo.MotionInfo
|
||||||
@@ -152,9 +157,14 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
|
|||||||
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
|
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
|
||||||
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_ANIMATOR_STATE_CHANGED:
|
case proto.CombatTypeArgument_COMBAT_TYPE_ARGUMENT_ANIMATOR_STATE_CHANGED:
|
||||||
evtAnimatorStateChangedInfo := new(proto.EvtAnimatorStateChangedInfo)
|
evtAnimatorStateChangedInfo := new(proto.EvtAnimatorStateChangedInfo)
|
||||||
err := pb.Unmarshal(entry.CombatData, evtAnimatorStateChangedInfo)
|
clientProtoObj := g.GetClientProtoObjByName("EvtAnimatorStateChangedInfo")
|
||||||
if err != nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("parse EvtAnimatorStateChangedInfo error: %v", err)
|
logger.Error("get client proto obj is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok := utils.UnmarshalProtoObj(evtAnimatorStateChangedInfo, clientProtoObj, entry.CombatData)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
logger.Debug("EvtAnimatorStateChangedInfo: %v", entry, player.PlayerID)
|
logger.Debug("EvtAnimatorStateChangedInfo: %v", entry, player.PlayerID)
|
||||||
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
|
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
|
||||||
@@ -240,9 +250,13 @@ func (g *GameManager) ClientAbilityChangeNotify(player *model.Player, payloadMsg
|
|||||||
switch abilityInvokeEntry.ArgumentType {
|
switch abilityInvokeEntry.ArgumentType {
|
||||||
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_META_ADD_NEW_ABILITY:
|
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_META_ADD_NEW_ABILITY:
|
||||||
abilityMetaAddAbility := new(proto.AbilityMetaAddAbility)
|
abilityMetaAddAbility := new(proto.AbilityMetaAddAbility)
|
||||||
err := pb.Unmarshal(abilityInvokeEntry.AbilityData, abilityMetaAddAbility)
|
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaAddAbility")
|
||||||
if err != nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("%v", err)
|
logger.Error("get client proto obj is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok := utils.UnmarshalProtoObj(abilityMetaAddAbility, clientProtoObj, abilityInvokeEntry.AbilityData)
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
worldAvatar := world.GetWorldAvatarByEntityId(abilityInvokeEntry.EntityId)
|
worldAvatar := world.GetWorldAvatarByEntityId(abilityInvokeEntry.EntityId)
|
||||||
@@ -252,9 +266,13 @@ func (g *GameManager) ClientAbilityChangeNotify(player *model.Player, payloadMsg
|
|||||||
worldAvatar.abilityList = append(worldAvatar.abilityList, abilityMetaAddAbility.Ability)
|
worldAvatar.abilityList = append(worldAvatar.abilityList, abilityMetaAddAbility.Ability)
|
||||||
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE:
|
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_META_MODIFIER_CHANGE:
|
||||||
abilityMetaModifierChange := new(proto.AbilityMetaModifierChange)
|
abilityMetaModifierChange := new(proto.AbilityMetaModifierChange)
|
||||||
err := pb.Unmarshal(abilityInvokeEntry.AbilityData, abilityMetaModifierChange)
|
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaModifierChange")
|
||||||
if err != nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("%v", err)
|
logger.Error("get client proto obj is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok := utils.UnmarshalProtoObj(abilityMetaModifierChange, clientProtoObj, abilityInvokeEntry.AbilityData)
|
||||||
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
abilityAppliedModifier := &proto.AbilityAppliedModifier{
|
abilityAppliedModifier := &proto.AbilityAppliedModifier{
|
||||||
|
|||||||
@@ -141,7 +141,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)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"hk4e/common/constant"
|
"hk4e/common/constant"
|
||||||
|
"hk4e/common/utils"
|
||||||
"hk4e/gdconf"
|
"hk4e/gdconf"
|
||||||
"hk4e/gs/model"
|
"hk4e/gs/model"
|
||||||
"hk4e/pkg/endec"
|
"hk4e/pkg/endec"
|
||||||
@@ -21,9 +22,13 @@ func (g *GameManager) HandleAbilityStamina(player *model.Player, entry *proto.Ab
|
|||||||
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA:
|
case proto.AbilityInvokeArgument_ABILITY_INVOKE_ARGUMENT_MIXIN_COST_STAMINA:
|
||||||
// 大剑重击 或 持续技能 耐力消耗
|
// 大剑重击 或 持续技能 耐力消耗
|
||||||
costStamina := new(proto.AbilityMixinCostStamina)
|
costStamina := new(proto.AbilityMixinCostStamina)
|
||||||
err := pb.Unmarshal(entry.AbilityData, costStamina)
|
clientProtoObj := g.GetClientProtoObjByName("AbilityMixinCostStamina")
|
||||||
if err != nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("unmarshal ability data err: %v", err)
|
logger.Error("get client proto obj is nil")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ok := utils.UnmarshalProtoObj(costStamina, clientProtoObj, entry.AbilityData)
|
||||||
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 处理持续耐力消耗
|
// 处理持续耐力消耗
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/vmihailenco/msgpack/v5"
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
"google.golang.org/protobuf/encoding/protojson"
|
"google.golang.org/protobuf/encoding/protojson"
|
||||||
pb "google.golang.org/protobuf/proto"
|
pb "google.golang.org/protobuf/proto"
|
||||||
@@ -37,45 +38,56 @@ func FastDeepCopy(dst, src any) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CopyProtoBufSameField(dst, src pb.Message) error {
|
func CopyProtoBufSameField(dst, src pb.Message) ([]string, error) {
|
||||||
date, err := protojson.Marshal(src)
|
date, err := protojson.Marshal(src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
|
||||||
jsonObj := make(map[string]any)
|
|
||||||
err = json.Unmarshal(date, &jsonObj)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
delList := make([]string, 0)
|
||||||
|
loopCount := 0
|
||||||
for {
|
for {
|
||||||
jsonData, err := json.Marshal(jsonObj)
|
loopCount++
|
||||||
if err != nil {
|
if loopCount > 1000 {
|
||||||
return err
|
return nil, errors.New("loop count limit")
|
||||||
}
|
}
|
||||||
err = protojson.Unmarshal(jsonData, dst)
|
err = protojson.Unmarshal(date, dst)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if !strings.Contains(err.Error(), "unknown field") {
|
if !strings.Contains(err.Error(), "unknown field") {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
split := strings.Split(err.Error(), "\"")
|
split := strings.Split(err.Error(), "\"")
|
||||||
if len(split) != 3 {
|
if len(split) != 3 {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
fieldName := split[1]
|
fieldName := split[1]
|
||||||
|
jsonObj := make(map[string]any)
|
||||||
|
err = json.Unmarshal(date, &jsonObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
DeleteAllKeyNameFromStringAnyMap(jsonObj, fieldName)
|
DeleteAllKeyNameFromStringAnyMap(jsonObj, fieldName)
|
||||||
|
delList = append(delList, fieldName)
|
||||||
|
date, err = json.Marshal(jsonObj)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return delList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteAllKeyNameFromStringAnyMap(src map[string]any, keyName string) {
|
func DeleteAllKeyNameFromStringAnyMap(src map[string]any, keyName string) {
|
||||||
for key, value := range src {
|
for key, value := range src {
|
||||||
v, ok := value.(map[string]any)
|
vm, ok := value.(map[string]any)
|
||||||
if ok {
|
if ok {
|
||||||
DeleteAllKeyNameFromStringAnyMap(v, keyName)
|
DeleteAllKeyNameFromStringAnyMap(vm, keyName)
|
||||||
|
}
|
||||||
|
vs, ok := value.([]any)
|
||||||
|
if ok {
|
||||||
|
DeleteAllKeyNameFromAnyList(vs, keyName)
|
||||||
}
|
}
|
||||||
if key == keyName {
|
if key == keyName {
|
||||||
delete(src, key)
|
delete(src, key)
|
||||||
@@ -83,6 +95,19 @@ func DeleteAllKeyNameFromStringAnyMap(src map[string]any, keyName string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeleteAllKeyNameFromAnyList(src []any, keyName string) {
|
||||||
|
for _, value := range src {
|
||||||
|
vm, ok := value.(map[string]any)
|
||||||
|
if ok {
|
||||||
|
DeleteAllKeyNameFromStringAnyMap(vm, keyName)
|
||||||
|
}
|
||||||
|
vs, ok := value.([]any)
|
||||||
|
if ok {
|
||||||
|
DeleteAllKeyNameFromAnyList(vs, keyName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func ConvBoolToInt64(v bool) int64 {
|
func ConvBoolToInt64(v bool) int64 {
|
||||||
if v {
|
if v {
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
Reference in New Issue
Block a user