package controller import ( "blazing/cool" "blazing/logic/service/common" "fmt" "strconv" "strings" "bytes" "context" "reflect" "github.com/gogf/gf/v2/os/glog" "github.com/lunixbochs/struc" ) var Maincontroller = &Controller{} //注入service // 分发cmd逻辑实现Controller type Controller struct { Port uint16 RPCClient struct { Kick func(uint32) error RegisterLogic func(uint16, uint16) error } } func ParseCmd[T any](a T, data []byte) T { // := info.NewLoginSidInfo() struc.Unpack(bytes.NewBuffer(data), &a) return a //fmt.Println(pinfo) //login.OnData_1001(pinfo, player) //fmt.Println(data) } func Init(isgame bool) { //默认初始化扫描 // 获取对象的反射值和类型 value := reflect.ValueOf(Maincontroller) // 获取类型 typ := value.Type() for i := 0; i < typ.NumMethod(); i++ { method := typ.Method(i) methodValue := value.MethodByName(method.Name) //fmt.Println("找到注册方法", method.Name) methodValue.Type().NumIn() for _, func_cmd := range getCmd(methodValue.Type().In(0)) { if func_cmd == 0 { //说明不是注册方法 glog.Warning(context.Background(), "方法参数必须包含CMD参数", method.Name, "跳过注册") continue } if !isgame && func_cmd > 1000 { //判断login服务器 continue } if isgame && func_cmd < 1000 { //判断login服务器 continue } glog.Debug(context.Background(), "注册方法", func_cmd, method.Name) _, ok := cool.CmdCache.LoadOrStore(func_cmd, cool.Cmd{ Func: methodValue, Req: methodValue.Type().In(0).Elem(), // Res: , }) //TODO 待实现对不同用户初始化方法以取消全局cmdcache if ok { //方法已存在init glog.Error(context.Background(), "方法已存在init,不会初始化后面的方法", func_cmd) } } } } var targetType = reflect.TypeOf(common.TomeeHeader{}) // 默认返回值(无匹配字段/解析失败时) const defaultCmdValue = 0 // getCmd 从结构体类型中提取绑定的cmd指令(递归查找嵌套结构体,支持值/指针类型的TomeeHeader) // 参数 typ:待解析的结构体类型(支持多层指针) // 返回值:解析到的cmd切片,无匹配/解析失败时返回[defaultCmdValue] func getCmd(typ reflect.Type) []uint32 { // 初始化目标类型(仅第一次调用时执行) // 1. 递归解引用所有指针类型(处理 *struct、**struct 等场景) for typ.Kind() == reflect.Ptr { typ = typ.Elem() } // 2. 非结构体类型直接返回默认值 if typ.Kind() != reflect.Struct { return []uint32{defaultCmdValue} } // 3. 遍历结构体字段,查找TomeeHeader字段并解析cmd for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) // 4. 调用解析函数,判断是否为目标类型并解析cmd cmdSlice, err := ParseCmdTagWithStructField(field) if err == nil { // 解析成功,直接返回结果 return cmdSlice } // 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 } } } // 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 }