2025-06-26 23:20:11 +08:00
|
|
|
|
package controller
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
2025-07-06 11:38:02 +08:00
|
|
|
|
"blazing/cool"
|
2025-11-19 16:11:02 +08:00
|
|
|
|
"blazing/logic/service/common"
|
2025-11-30 04:57:48 +00:00
|
|
|
|
"fmt"
|
|
|
|
|
|
"strconv"
|
2025-09-14 01:35:16 +08:00
|
|
|
|
|
2025-08-28 19:17:03 +00:00
|
|
|
|
"strings"
|
2025-06-26 23:20:11 +08:00
|
|
|
|
|
|
|
|
|
|
"bytes"
|
|
|
|
|
|
"context"
|
|
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
|
|
|
|
"github.com/gogf/gf/v2/os/glog"
|
|
|
|
|
|
"github.com/lunixbochs/struc"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
var Maincontroller = &Controller{} //注入service
|
2025-10-10 23:59:54 +08:00
|
|
|
|
|
2025-06-26 23:20:11 +08:00
|
|
|
|
// 分发cmd逻辑实现Controller
|
|
|
|
|
|
type Controller struct {
|
2025-07-15 13:51:10 +00:00
|
|
|
|
Port uint16
|
2025-07-06 19:31:30 +08:00
|
|
|
|
RPCClient struct {
|
2025-07-15 14:00:32 +00:00
|
|
|
|
Kick func(uint32) error
|
2025-07-06 22:58:39 +08:00
|
|
|
|
|
2025-07-17 05:20:30 +08:00
|
|
|
|
RegisterLogic func(uint16, uint16) error
|
2025-07-06 19:31:30 +08:00
|
|
|
|
}
|
2025-06-26 23:20:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-27 20:52:15 +00:00
|
|
|
|
func ParseCmd[T any](a T, data []byte) T {
|
2025-06-26 23:20:11 +08:00
|
|
|
|
// := info.NewLoginSidInfo()
|
|
|
|
|
|
struc.Unpack(bytes.NewBuffer(data), &a)
|
|
|
|
|
|
return a
|
|
|
|
|
|
//fmt.Println(pinfo)
|
|
|
|
|
|
//login.OnData_1001(pinfo, player)
|
|
|
|
|
|
//fmt.Println(data)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-08 17:03:43 +08:00
|
|
|
|
func Init(isgame bool) { //默认初始化扫描
|
|
|
|
|
|
|
2025-06-26 23:20:11 +08:00
|
|
|
|
// 获取对象的反射值和类型
|
2025-07-06 19:31:30 +08:00
|
|
|
|
value := reflect.ValueOf(Maincontroller)
|
2025-06-26 23:20:11 +08:00
|
|
|
|
|
|
|
|
|
|
// 获取类型
|
|
|
|
|
|
typ := value.Type()
|
|
|
|
|
|
|
|
|
|
|
|
for i := 0; i < typ.NumMethod(); i++ {
|
|
|
|
|
|
method := typ.Method(i)
|
|
|
|
|
|
|
|
|
|
|
|
methodValue := value.MethodByName(method.Name)
|
2025-08-30 00:36:08 +08:00
|
|
|
|
//fmt.Println("找到注册方法", method.Name)
|
2025-06-26 23:20:11 +08:00
|
|
|
|
methodValue.Type().NumIn()
|
|
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
for _, func_cmd := range getCmd(methodValue.Type().In(0)) {
|
2025-08-28 19:17:03 +00:00
|
|
|
|
if func_cmd == 0 { //说明不是注册方法
|
2025-11-11 01:10:26 +08:00
|
|
|
|
glog.Warning(context.Background(), "方法参数必须包含CMD参数", method.Name, "跳过注册")
|
2025-08-28 19:17:03 +00:00
|
|
|
|
continue
|
|
|
|
|
|
}
|
2025-06-26 23:20:11 +08:00
|
|
|
|
|
2025-12-08 17:03:43 +08:00
|
|
|
|
if !isgame && func_cmd > 1000 { //判断login服务器
|
2025-08-28 19:17:03 +00:00
|
|
|
|
continue
|
2025-07-06 11:38:02 +08:00
|
|
|
|
|
2025-08-28 19:17:03 +00:00
|
|
|
|
}
|
2025-07-06 11:38:02 +08:00
|
|
|
|
|
2025-12-08 17:03:43 +08:00
|
|
|
|
if isgame && func_cmd < 1000 { //判断login服务器
|
2025-08-28 19:17:03 +00:00
|
|
|
|
continue
|
2025-06-26 23:20:11 +08:00
|
|
|
|
|
2025-08-28 19:17:03 +00:00
|
|
|
|
}
|
|
|
|
|
|
glog.Debug(context.Background(), "注册方法", func_cmd, method.Name)
|
2025-11-30 04:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
_, ok := cool.CmdCache.LoadOrStore(func_cmd, cool.Cmd{
|
|
|
|
|
|
Func: methodValue,
|
|
|
|
|
|
Req: methodValue.Type().In(0).Elem(),
|
|
|
|
|
|
// Res: ,
|
|
|
|
|
|
}) //TODO 待实现对不同用户初始化方法以取消全局cmdcache
|
2025-08-28 19:17:03 +00:00
|
|
|
|
|
|
|
|
|
|
if ok { //方法已存在init
|
|
|
|
|
|
glog.Error(context.Background(), "方法已存在init,不会初始化后面的方法", func_cmd)
|
|
|
|
|
|
}
|
2025-06-26 23:20:11 +08:00
|
|
|
|
}
|
2025-08-28 19:17:03 +00:00
|
|
|
|
|
2025-06-26 23:20:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-07-06 11:38:02 +08:00
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
var targetType = reflect.TypeOf(common.TomeeHeader{})
|
|
|
|
|
|
|
|
|
|
|
|
// 默认返回值(无匹配字段/解析失败时)
|
|
|
|
|
|
const defaultCmdValue = 0
|
|
|
|
|
|
|
|
|
|
|
|
// getCmd 从结构体类型中提取绑定的cmd指令(递归查找嵌套结构体,支持值/指针类型的TomeeHeader)
|
2025-06-26 23:20:11 +08:00
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
// 参数 typ:待解析的结构体类型(支持多层指针)
|
|
|
|
|
|
// 返回值:解析到的cmd切片,无匹配/解析失败时返回[defaultCmdValue]
|
|
|
|
|
|
func getCmd(typ reflect.Type) []uint32 {
|
|
|
|
|
|
// 初始化目标类型(仅第一次调用时执行)
|
2025-07-15 11:24:49 +08:00
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
// 1. 递归解引用所有指针类型(处理 *struct、**struct 等场景)
|
|
|
|
|
|
for typ.Kind() == reflect.Ptr {
|
|
|
|
|
|
typ = typ.Elem()
|
2025-06-26 23:20:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
// 2. 非结构体类型直接返回默认值
|
|
|
|
|
|
if typ.Kind() != reflect.Struct {
|
|
|
|
|
|
return []uint32{defaultCmdValue}
|
|
|
|
|
|
}
|
2025-06-26 23:20:11 +08:00
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
// 3. 遍历结构体字段,查找TomeeHeader字段并解析cmd
|
|
|
|
|
|
for i := 0; i < typ.NumField(); i++ {
|
|
|
|
|
|
field := typ.Field(i)
|
2025-06-26 23:20:11 +08:00
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
// 4. 调用解析函数,判断是否为目标类型并解析cmd
|
|
|
|
|
|
cmdSlice, err := ParseCmdTagWithStructField(field)
|
|
|
|
|
|
if err == nil { // 解析成功,直接返回结果
|
|
|
|
|
|
return cmdSlice
|
2025-06-26 23:20:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-30 04:57:48 +00:00
|
|
|
|
// 5. 递归处理嵌套结构体(值/指针类型)
|
|
|
|
|
|
nestedTyp := field.Type
|
|
|
|
|
|
if nestedTyp.Kind() == reflect.Ptr {
|
|
|
|
|
|
nestedTyp = nestedTyp.Elem()
|
|
|
|
|
|
}
|
|
|
|
|
|
if nestedTyp.Kind() == reflect.Struct {
|
|
|
|
|
|
// 递归查找,找到有效cmd则立即返回
|
|
|
|
|
|
if nestedCmd := getCmd(nestedTyp); len(nestedCmd) > 0 && nestedCmd[0] != defaultCmdValue {
|
|
|
|
|
|
return nestedCmd
|
2025-06-26 23:20:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-30 04:57:48 +00:00
|
|
|
|
|
|
|
|
|
|
// 6. 未找到目标字段/所有解析失败,返回默认值
|
|
|
|
|
|
return []uint32{defaultCmdValue}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ParseCmdTagWithStructField 校验字段是否为TomeeHeader(值/指针)并解析cmd标签
|
|
|
|
|
|
// 参数 field:结构体字段元信息
|
|
|
|
|
|
// 返回值:解析后的cmd切片,非目标类型/解析失败返回错误
|
|
|
|
|
|
func ParseCmdTagWithStructField(field reflect.StructField) ([]uint32, error) {
|
|
|
|
|
|
|
|
|
|
|
|
// 判断字段类型是否为 TomeeHeader 或 *TomeeHeader
|
|
|
|
|
|
var isTomeeHeader bool
|
|
|
|
|
|
switch {
|
|
|
|
|
|
case field.Type == targetType: // 值类型
|
|
|
|
|
|
isTomeeHeader = true
|
|
|
|
|
|
case field.Type.Kind() == reflect.Ptr && field.Type.Elem() == targetType: // 指针类型
|
|
|
|
|
|
isTomeeHeader = true
|
|
|
|
|
|
default:
|
|
|
|
|
|
isTomeeHeader = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 非目标类型返回错误
|
|
|
|
|
|
if !isTomeeHeader {
|
|
|
|
|
|
return nil, fmt.Errorf("field %s (type: %v) is not common.TomeeHeader or *common.TomeeHeader",
|
|
|
|
|
|
field.Name, field.Type)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 提取cmd标签
|
|
|
|
|
|
cmdStr := field.Tag.Get("cmd")
|
|
|
|
|
|
if cmdStr == "" {
|
|
|
|
|
|
return nil, fmt.Errorf("field %s cmd tag is empty", field.Name)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 高性能解析标签为uint32切片(替代gconv,减少第三方依赖且可控)
|
|
|
|
|
|
parts := strings.Split(cmdStr, "|")
|
|
|
|
|
|
result := make([]uint32, 0, len(parts))
|
|
|
|
|
|
for idx, s := range parts {
|
|
|
|
|
|
// 去除空白字符(兼容标签中意外的空格)
|
|
|
|
|
|
s = strings.TrimSpace(s)
|
|
|
|
|
|
if s == "" {
|
|
|
|
|
|
return nil, fmt.Errorf("field %s cmd tag part %d is empty", field.Name, idx)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 手动解析uint32,比gconv更可控,避免隐式转换问题
|
|
|
|
|
|
num, err := strconv.ParseUint(s, 10, 32)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, fmt.Errorf("field %s cmd tag part %d parse error: %v (value: %s)",
|
|
|
|
|
|
field.Name, idx, err, s)
|
|
|
|
|
|
}
|
|
|
|
|
|
result = append(result, uint32(num))
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result, nil
|
2025-06-26 23:20:11 +08:00
|
|
|
|
}
|