mirror of
https://github.com/FlourishingWorld/hk4e.git
synced 2026-03-01 00:35:36 +08:00
优化协议代理代码生成
This commit is contained in:
@@ -2,11 +2,15 @@ CUR_DIR=$(shell pwd)
|
|||||||
|
|
||||||
VERSION=1.0.0
|
VERSION=1.0.0
|
||||||
|
|
||||||
|
.PHONY: all
|
||||||
|
all: build
|
||||||
|
|
||||||
# 清理
|
# 清理
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
rm -rf ./bin
|
rm -rf ./bin
|
||||||
rm -rf ./protocol/proto
|
rm -rf ./protocol/proto
|
||||||
|
rm -rf ./gate/client_proto/client_proto_gen.go
|
||||||
|
|
||||||
# 构建服务器二进制文件
|
# 构建服务器二进制文件
|
||||||
.PHONY: build
|
.PHONY: build
|
||||||
@@ -92,4 +96,4 @@ gen_csv:
|
|||||||
# 生成客户端协议代理功能所需的代码
|
# 生成客户端协议代理功能所需的代码
|
||||||
.PHONY: gen_client_proto
|
.PHONY: gen_client_proto
|
||||||
gen_client_proto:
|
gen_client_proto:
|
||||||
cd gate/client_proto && go test -v -run TestClientProtoGen .
|
cd gate/client_proto && rm -rf client_proto_gen.go && go test -v -run TestClientProtoGen .
|
||||||
|
|||||||
@@ -301,17 +301,15 @@ func (f *FightRoutine) getAllPlayer(entityMap map[uint32]*Entity) []uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ClientCmdProtoMap *client_proto.ClientCmdProtoMap
|
var ClientCmdProtoMap *client_proto.ClientCmdProtoMap
|
||||||
var ClientCmdProtoMapRefValue reflect.Value
|
|
||||||
|
|
||||||
func initClientCmdProtoMap() {
|
func initClientCmdProtoMap() {
|
||||||
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
ClientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
ClientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
||||||
ClientCmdProtoMapRefValue = reflect.ValueOf(ClientCmdProtoMap)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetClientProtoObjByName(protoObjName string) pb.Message {
|
func GetClientProtoObjByName(protoObjName string) pb.Message {
|
||||||
fn := ClientCmdProtoMapRefValue.MethodByName("GetClientProtoObjByName")
|
fn := ClientCmdProtoMap.RefValue.MethodByName("GetClientProtoObjByName")
|
||||||
ret := fn.Call([]reflect.Value{reflect.ValueOf(protoObjName)})
|
ret := fn.Call([]reflect.Value{reflect.ValueOf(protoObjName)})
|
||||||
obj := ret[0].Interface()
|
obj := ret[0].Interface()
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package client_proto
|
package client_proto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"reflect"
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"hk4e/pkg/logger"
|
"hk4e/pkg/logger"
|
||||||
)
|
)
|
||||||
@@ -11,31 +9,16 @@ import (
|
|||||||
type ClientCmdProtoMap struct {
|
type ClientCmdProtoMap struct {
|
||||||
clientCmdIdCmdNameMap map[uint16]string
|
clientCmdIdCmdNameMap map[uint16]string
|
||||||
clientCmdNameCmdIdMap map[string]uint16
|
clientCmdNameCmdIdMap map[string]uint16
|
||||||
|
RefValue reflect.Value
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClientCmdProtoMap() (r *ClientCmdProtoMap) {
|
func NewClientCmdProtoMap() (r *ClientCmdProtoMap) {
|
||||||
r = new(ClientCmdProtoMap)
|
r = new(ClientCmdProtoMap)
|
||||||
r.clientCmdIdCmdNameMap = make(map[uint16]string)
|
r.clientCmdIdCmdNameMap = make(map[uint16]string)
|
||||||
r.clientCmdNameCmdIdMap = make(map[string]uint16)
|
r.clientCmdNameCmdIdMap = make(map[string]uint16)
|
||||||
clientCmdFile, err := os.ReadFile("./client_cmd.csv")
|
r.RefValue = reflect.ValueOf(r)
|
||||||
if err != nil {
|
fn := r.RefValue.MethodByName("LoadClientCmdIdAndCmdName")
|
||||||
panic(err)
|
fn.Call([]reflect.Value{})
|
||||||
}
|
|
||||||
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
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ func TestClientProtoGen(t *testing.T) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
nameList := make([]string, 0)
|
protoObjNameList := make([]string, 0)
|
||||||
for _, entry := range dir {
|
for _, entry := range dir {
|
||||||
if entry.IsDir() {
|
if entry.IsDir() {
|
||||||
continue
|
continue
|
||||||
@@ -21,17 +21,41 @@ func TestClientProtoGen(t *testing.T) {
|
|||||||
if len(split) < 2 || split[len(split)-1] != "proto" {
|
if len(split) < 2 || split[len(split)-1] != "proto" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
nameList = append(nameList, split[len(split)-2])
|
protoObjNameList = append(protoObjNameList, split[len(split)-2])
|
||||||
}
|
}
|
||||||
|
// 生成初始化cmdId和cmdName的方法
|
||||||
|
clientCmdFile, err := os.ReadFile("./proto/client_cmd.csv")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
clientCmdData := string(clientCmdFile)
|
||||||
|
clientCmdLineList := strings.Split(clientCmdData, "\n")
|
||||||
|
// 生成代码文件
|
||||||
fileData := "package client_proto\n"
|
fileData := "package client_proto\n"
|
||||||
fileData += "\n"
|
fileData += "\n"
|
||||||
fileData += "import (\n"
|
fileData += "import (\n"
|
||||||
fileData += "\t\"hk4e/gate/client_proto/proto\"\n"
|
fileData += "\t\"hk4e/gate/client_proto/proto\"\n"
|
||||||
fileData += ")\n"
|
fileData += ")\n"
|
||||||
fileData += "\n"
|
fileData += "\n"
|
||||||
|
fileData += "func (c *ClientCmdProtoMap) LoadClientCmdIdAndCmdName() {\n"
|
||||||
|
for _, clientCmdLine := range clientCmdLineList {
|
||||||
|
if clientCmdLine == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
item := strings.Split(clientCmdLine, ",")
|
||||||
|
if len(item) != 2 {
|
||||||
|
panic("parse client cmd file error")
|
||||||
|
}
|
||||||
|
cmdName := item[0]
|
||||||
|
cmdId := item[1]
|
||||||
|
fileData += "\tc.clientCmdIdCmdNameMap[uint16(" + cmdId + ")] = \"" + cmdName + "\"\n"
|
||||||
|
fileData += "\tc.clientCmdNameCmdIdMap[\"" + cmdName + "\"] = uint16(" + cmdId + ")\n"
|
||||||
|
}
|
||||||
|
fileData += "}\n"
|
||||||
|
fileData += "\n"
|
||||||
fileData += "func (c *ClientCmdProtoMap) GetClientProtoObjByName(protoObjName string) any {\n"
|
fileData += "func (c *ClientCmdProtoMap) GetClientProtoObjByName(protoObjName string) any {\n"
|
||||||
fileData += "\tswitch protoObjName {\n"
|
fileData += "\tswitch protoObjName {\n"
|
||||||
for _, protoObjName := range nameList {
|
for _, protoObjName := range protoObjNameList {
|
||||||
fileData += "\tcase \"" + protoObjName + "\":\n\t\treturn new(proto." + protoObjName + ")\n"
|
fileData += "\tcase \"" + protoObjName + "\":\n\t\treturn new(proto." + protoObjName + ")\n"
|
||||||
}
|
}
|
||||||
fileData += "\tdefault:\n"
|
fileData += "\tdefault:\n"
|
||||||
@@ -62,6 +86,7 @@ func TestClientProtoGen(t *testing.T) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
enumName := split[1]
|
enumName := split[1]
|
||||||
|
// 从protocol/proto_hk4e下复制同名的枚举类替换掉原proto文件里的内容
|
||||||
refEnum := FindEnumInDirFile("../../protocol/proto_hk4e", enumName)
|
refEnum := FindEnumInDirFile("../../protocol/proto_hk4e", enumName)
|
||||||
if refEnum == nil {
|
if refEnum == nil {
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -38,7 +37,6 @@ type KcpConnectManager struct {
|
|||||||
kcpEventOutput chan *KcpEvent
|
kcpEventOutput chan *KcpEvent
|
||||||
serverCmdProtoMap *cmd.CmdProtoMap
|
serverCmdProtoMap *cmd.CmdProtoMap
|
||||||
clientCmdProtoMap *client_proto.ClientCmdProtoMap
|
clientCmdProtoMap *client_proto.ClientCmdProtoMap
|
||||||
clientCmdProtoMapRefValue reflect.Value
|
|
||||||
messageQueue *mq.MessageQueue
|
messageQueue *mq.MessageQueue
|
||||||
localMsgOutput chan *ProtoMsg
|
localMsgOutput chan *ProtoMsg
|
||||||
createSessionChan chan *Session
|
createSessionChan chan *Session
|
||||||
@@ -60,7 +58,6 @@ func NewKcpConnectManager(messageQueue *mq.MessageQueue, discovery *rpc.Discover
|
|||||||
r.serverCmdProtoMap = cmd.NewCmdProtoMap()
|
r.serverCmdProtoMap = cmd.NewCmdProtoMap()
|
||||||
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
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)
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ func (k *KcpConnectManager) encodeProtoToPayload(protoObj pb.Message) (cmdId uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (k *KcpConnectManager) getClientProtoObjByName(protoObjName string) pb.Message {
|
func (k *KcpConnectManager) getClientProtoObjByName(protoObjName string) pb.Message {
|
||||||
fn := k.clientCmdProtoMapRefValue.MethodByName("GetClientProtoObjByName")
|
fn := k.clientCmdProtoMap.RefValue.MethodByName("GetClientProtoObjByName")
|
||||||
ret := fn.Call([]reflect.Value{reflect.ValueOf(protoObjName)})
|
ret := fn.Call([]reflect.Value{reflect.ValueOf(protoObjName)})
|
||||||
obj := ret[0].Interface()
|
obj := ret[0].Interface()
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
appConfig "hk4e/common/config"
|
"hk4e/common/config"
|
||||||
"hk4e/common/mq"
|
"hk4e/common/mq"
|
||||||
"hk4e/gate/client_proto"
|
"hk4e/gate/client_proto"
|
||||||
"hk4e/gate/kcp"
|
"hk4e/gate/kcp"
|
||||||
@@ -48,7 +48,6 @@ type GameManager struct {
|
|||||||
dao *dao.Dao
|
dao *dao.Dao
|
||||||
snowflake *alg.SnowflakeWorker
|
snowflake *alg.SnowflakeWorker
|
||||||
clientCmdProtoMap *client_proto.ClientCmdProtoMap
|
clientCmdProtoMap *client_proto.ClientCmdProtoMap
|
||||||
clientCmdProtoMapRefValue reflect.Value
|
|
||||||
gsId uint32
|
gsId uint32
|
||||||
gsAppid string
|
gsAppid string
|
||||||
mainGsAppid string
|
mainGsAppid string
|
||||||
@@ -60,9 +59,8 @@ func NewGameManager(dao *dao.Dao, messageQueue *mq.MessageQueue, gsId uint32, gs
|
|||||||
r.dao = dao
|
r.dao = dao
|
||||||
MESSAGE_QUEUE = messageQueue
|
MESSAGE_QUEUE = messageQueue
|
||||||
r.snowflake = alg.NewSnowflakeWorker(int64(gsId))
|
r.snowflake = alg.NewSnowflakeWorker(int64(gsId))
|
||||||
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
r.clientCmdProtoMap = client_proto.NewClientCmdProtoMap()
|
||||||
r.clientCmdProtoMapRefValue = reflect.ValueOf(r.clientCmdProtoMap)
|
|
||||||
// 反射调用的方法在启动时测试是否正常防止中途panic
|
// 反射调用的方法在启动时测试是否正常防止中途panic
|
||||||
r.GetClientProtoObjByName("PingReq")
|
r.GetClientProtoObjByName("PingReq")
|
||||||
}
|
}
|
||||||
@@ -394,7 +392,7 @@ func (g *GameManager) DisconnectPlayer(userId uint32, reason uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *GameManager) GetClientProtoObjByName(protoObjName string) pb.Message {
|
func (g *GameManager) GetClientProtoObjByName(protoObjName string) pb.Message {
|
||||||
fn := g.clientCmdProtoMapRefValue.MethodByName("GetClientProtoObjByName")
|
fn := g.clientCmdProtoMap.RefValue.MethodByName("GetClientProtoObjByName")
|
||||||
ret := fn.Call([]reflect.Value{reflect.ValueOf(protoObjName)})
|
ret := fn.Call([]reflect.Value{reflect.ValueOf(protoObjName)})
|
||||||
obj := ret[0].Interface()
|
obj := ret[0].Interface()
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import (
|
import (
|
||||||
appConfig "hk4e/common/config"
|
"hk4e/common/config"
|
||||||
"hk4e/common/constant"
|
"hk4e/common/constant"
|
||||||
"hk4e/common/utils"
|
"hk4e/common/utils"
|
||||||
"hk4e/gs/model"
|
"hk4e/gs/model"
|
||||||
@@ -104,7 +104,7 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
|
|||||||
switch entry.ArgumentType {
|
switch entry.ArgumentType {
|
||||||
case proto.CombatTypeArgument_COMBAT_EVT_BEING_HIT:
|
case proto.CombatTypeArgument_COMBAT_EVT_BEING_HIT:
|
||||||
hitInfo := new(proto.EvtBeingHitInfo)
|
hitInfo := new(proto.EvtBeingHitInfo)
|
||||||
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
clientProtoObj := g.GetClientProtoObjByName("EvtBeingHitInfo")
|
clientProtoObj := g.GetClientProtoObjByName("EvtBeingHitInfo")
|
||||||
if clientProtoObj == nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("get client proto obj is nil")
|
logger.Error("get client proto obj is nil")
|
||||||
@@ -161,7 +161,7 @@ func (g *GameManager) CombatInvocationsNotify(player *model.Player, payloadMsg p
|
|||||||
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
|
player.CombatInvokeHandler.AddEntry(entry.ForwardType, entry)
|
||||||
case proto.CombatTypeArgument_ENTITY_MOVE:
|
case proto.CombatTypeArgument_ENTITY_MOVE:
|
||||||
entityMoveInfo := new(proto.EntityMoveInfo)
|
entityMoveInfo := new(proto.EntityMoveInfo)
|
||||||
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
clientProtoObj := g.GetClientProtoObjByName("EntityMoveInfo")
|
clientProtoObj := g.GetClientProtoObjByName("EntityMoveInfo")
|
||||||
if clientProtoObj == nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("get client proto obj is nil")
|
logger.Error("get client proto obj is nil")
|
||||||
@@ -227,7 +227,7 @@ 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_ANIMATOR_STATE_CHANGED:
|
case proto.CombatTypeArgument_COMBAT_ANIMATOR_STATE_CHANGED:
|
||||||
evtAnimatorStateChangedInfo := new(proto.EvtAnimatorStateChangedInfo)
|
evtAnimatorStateChangedInfo := new(proto.EvtAnimatorStateChangedInfo)
|
||||||
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
clientProtoObj := g.GetClientProtoObjByName("EvtAnimatorStateChangedInfo")
|
clientProtoObj := g.GetClientProtoObjByName("EvtAnimatorStateChangedInfo")
|
||||||
if clientProtoObj == nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("get client proto obj is nil")
|
logger.Error("get client proto obj is nil")
|
||||||
@@ -386,7 +386,7 @@ func (g *GameManager) ClientAbilityChangeNotify(player *model.Player, payloadMsg
|
|||||||
switch abilityInvokeEntry.ArgumentType {
|
switch abilityInvokeEntry.ArgumentType {
|
||||||
case proto.AbilityInvokeArgument_ABILITY_META_ADD_NEW_ABILITY:
|
case proto.AbilityInvokeArgument_ABILITY_META_ADD_NEW_ABILITY:
|
||||||
abilityMetaAddAbility := new(proto.AbilityMetaAddAbility)
|
abilityMetaAddAbility := new(proto.AbilityMetaAddAbility)
|
||||||
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaAddAbility")
|
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaAddAbility")
|
||||||
if clientProtoObj == nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("get client proto obj is nil")
|
logger.Error("get client proto obj is nil")
|
||||||
@@ -413,7 +413,7 @@ 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_META_MODIFIER_CHANGE:
|
case proto.AbilityInvokeArgument_ABILITY_META_MODIFIER_CHANGE:
|
||||||
abilityMetaModifierChange := new(proto.AbilityMetaModifierChange)
|
abilityMetaModifierChange := new(proto.AbilityMetaModifierChange)
|
||||||
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaModifierChange")
|
clientProtoObj := g.GetClientProtoObjByName("AbilityMetaModifierChange")
|
||||||
if clientProtoObj == nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("get client proto obj is nil")
|
logger.Error("get client proto obj is nil")
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
appConfig "hk4e/common/config"
|
"hk4e/common/config"
|
||||||
"hk4e/common/constant"
|
"hk4e/common/constant"
|
||||||
"hk4e/common/utils"
|
"hk4e/common/utils"
|
||||||
"hk4e/gdconf"
|
"hk4e/gdconf"
|
||||||
@@ -23,7 +23,7 @@ func (g *GameManager) HandleAbilityStamina(player *model.Player, entry *proto.Ab
|
|||||||
case proto.AbilityInvokeArgument_ABILITY_MIXIN_COST_STAMINA:
|
case proto.AbilityInvokeArgument_ABILITY_MIXIN_COST_STAMINA:
|
||||||
// 大剑重击 或 持续技能 耐力消耗
|
// 大剑重击 或 持续技能 耐力消耗
|
||||||
costStamina := new(proto.AbilityMixinCostStamina)
|
costStamina := new(proto.AbilityMixinCostStamina)
|
||||||
if appConfig.CONF.Hk4e.ClientProtoProxyEnable {
|
if config.CONF.Hk4e.ClientProtoProxyEnable {
|
||||||
clientProtoObj := g.GetClientProtoObjByName("AbilityMixinCostStamina")
|
clientProtoObj := g.GetClientProtoObjByName("AbilityMixinCostStamina")
|
||||||
if clientProtoObj == nil {
|
if clientProtoObj == nil {
|
||||||
logger.Error("get client proto obj is nil")
|
logger.Error("get client proto obj is nil")
|
||||||
|
|||||||
Reference in New Issue
Block a user