diff --git a/common/cool/global.go b/common/cool/global.go index 484282c4..47514015 100644 --- a/common/cool/global.go +++ b/common/cool/global.go @@ -15,8 +15,14 @@ import ( // var Limiter = limit.New() var ctx = context.TODO() +type Cmd struct { + Func reflect.Value //方法函数 + Req reflect.Type //请求体 + //Res reflect.Value //返回体 +} + var ( - CmdCache = &utils.SyncMap[uint32, reflect.Value]{} //命令缓存 + CmdCache = &utils.SyncMap[uint32, Cmd]{} //命令缓存 Loger = glog.New() Cron = cronex.New() //时间轮 diff --git a/common/data/xmlres/xml_test.go b/common/data/xmlres/xml_test.go index 620e3836..bdaa8f98 100644 --- a/common/data/xmlres/xml_test.go +++ b/common/data/xmlres/xml_test.go @@ -49,7 +49,7 @@ var s = ` func Test_main(t *testing.T) { - initfile() +// initfile() fmt.Println(SkillMap[10073]) } diff --git a/logic/controller/controller.go b/logic/controller/controller.go index 9751ad0f..11e68c0d 100644 --- a/logic/controller/controller.go +++ b/logic/controller/controller.go @@ -3,6 +3,8 @@ package controller import ( "blazing/cool" "blazing/logic/service/common" + "fmt" + "strconv" "strings" @@ -12,15 +14,10 @@ import ( "github.com/gogf/gf/v2/os/gcmd" "github.com/gogf/gf/v2/os/glog" - "github.com/gogf/gf/v2/util/gconv" "github.com/lunixbochs/struc" ) -var Maincontroller = NewController() //注入service - -func NewController() *Controller { - return &Controller{} -} +var Maincontroller = &Controller{} //注入service // 分发cmd逻辑实现Controller type Controller struct { @@ -57,7 +54,7 @@ func init() { //默认初始化扫描 //fmt.Println("找到注册方法", method.Name) methodValue.Type().NumIn() - for _, func_cmd := range getcmd(methodValue.Type().In(0)) { + for _, func_cmd := range getCmd(methodValue.Type().In(0)) { if func_cmd == 0 { //说明不是注册方法 glog.Warning(context.Background(), "方法参数必须包含CMD参数", method.Name, "跳过注册") continue @@ -73,8 +70,12 @@ func init() { //默认初始化扫描 } glog.Debug(context.Background(), "注册方法", func_cmd, method.Name) - // fmt.Println(methodValue.Interface().(func(gnet.Conn, player.TomeeHeader))) - _, ok := cool.CmdCache.LoadOrStore(func_cmd, methodValue) //TODO 待实现对不同用户初始化方法以取消全局cmdcache + + _, 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) @@ -84,41 +85,101 @@ func init() { //默认初始化扫描 } } -func getcmd(t reflect.Type) []uint32 { - // 处理指针类型 - if t.Kind() == reflect.Ptr { - t = t.Elem() // 获取指针指向的类型 +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() } - // 确保是结构体 - if t.Kind() != reflect.Struct { - - return []uint32{0} + // 2. 非结构体类型直接返回默认值 + if typ.Kind() != reflect.Struct { + return []uint32{defaultCmdValue} } - // 遍历结构体字段 - // fmt.Printf("结构体 %s 的字段信息:\n", t.Name()) - for i := 0; i < t.NumField(); i++ { - field := t.Field(i) - //fmt.Printf("- 字段名: %s\n", field.Name) - //fmt.Printf(" 类型: %v\n", field.Type) - if field.Type == reflect.TypeOf(common.TomeeHeader{}) { - // fmt.Println(reflect.ValueOf(field)) - - return gconv.SliceUint32(strings.Split(field.Tag.Get("cmd"), "|")) + // 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 } - if field.Type.Kind() == reflect.Struct || - (field.Type.Kind() == reflect.Ptr && field.Type.Elem().Kind() == reflect.Struct) { - - nestedType := field.Type - if nestedType.Kind() == reflect.Ptr { - nestedType = nestedType.Elem() + // 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 } - getcmd(nestedType) } - // fmt.Println() } - return []uint32{0} + + // 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 } diff --git a/logic/service/fight/input/input.go b/logic/service/fight/input/input.go index 7bb3c951..ac8cae10 100644 --- a/logic/service/fight/input/input.go +++ b/logic/service/fight/input/input.go @@ -3,7 +3,6 @@ package input import ( "blazing/common/data/xmlres" "fmt" - "sort" "blazing/logic/service/common" "blazing/logic/service/fight/action" @@ -58,33 +57,21 @@ func NewInput(c common.FightI, p common.PlayerI) *Input { } +// 非原地交换:收集非0血量精灵 + 0血量精灵,拼接后返回 func (our *Input) SortPet() { - sort.Slice(our.AllPet, func(i, j int) bool { - x, y := our.AllPet[i], our.AllPet[j] - // 若x血量>0且y血量=0,则x排在前 - if x.Info.Hp > 0 && y.Info.Hp <= 0 { - return true - } - // 若x血量=0且y血量>0,则x排在后 - if x.Info.Hp <= 0 && y.Info.Hp > 0 { - return false - } - // 同类型(都>0或都=0)保持原有顺序 - return i < j - }) - for _, v := range our.AllPet { - if v.Info.Hp == 0 { + var nonZeroHP []*info.BattlePetEntity // 收集血量>0的精灵(保持原顺序) + var zeroHP []*info.BattlePetEntity // 收集血量=0的精灵(保持原顺序) - v.NotAlive = true - - } else { - for _, e1 := range v.Info.EffectInfo { + // 线性遍历一次,分类收集 + for _, s := range our.AllPet { + if s.HP > 0 { + for _, e1 := range s.Info.EffectInfo { t := Geteffect(EffectType.NewSel, e1.EID) if t != nil { ef := t.ID() fmt.Println("初始化特性", ef.Suffix()) - ef.SetCatchTime(v.Info.CatchTime) + ef.SetCatchTime(s.Info.CatchTime) t.ID(ef) @@ -94,10 +81,17 @@ func (our *Input) SortPet() { } } + nonZeroHP = append(nonZeroHP, s) + } else { + s.NotAlive = true + zeroHP = append(zeroHP, s) } - } + + // 拼接:非0血量精灵在前,0血量精灵在后 + our.AllPet = append(nonZeroHP, zeroHP...) } + func (our *Input) GetPetInfo() *info.BattlePetEntity { return our.CurrentPet diff --git a/logic/service/player/pack.go b/logic/service/player/pack.go index 572569f9..65066318 100644 --- a/logic/service/player/pack.go +++ b/logic/service/player/pack.go @@ -63,7 +63,7 @@ func (h *ClientData) Recv(data common.TomeeHeader) { //funct := cmdlister.Type().NumIn() // 如果需要可设置的变量(用于修改值),创建指针并解引用 - ptrValue := reflect.New(cmdlister.Type().In(0).Elem()) + ptrValue := reflect.New(cmdlister.Req) tt1 := ptrValue.Elem().Addr().Interface() // fmt.Println(tt1) @@ -76,9 +76,7 @@ func (h *ClientData) Recv(data common.TomeeHeader) { ptrValue1 := ptrValue.Elem().Addr() // 设置 Name 字段 nameField := ptrValue.Elem().Field(0) //首个为header - if nameField.IsValid() && nameField.CanSet() { - nameField.Set(reflect.ValueOf(data)) - } + nameField.Set(reflect.ValueOf(data)) if data.CMD > 1001 { //if cmdlister.Type().In(1) == reflect.TypeOf(&Player{}) { //t := GetPlayer(c, data.UserID) @@ -91,7 +89,7 @@ func (h *ClientData) Recv(data common.TomeeHeader) { params = append(params, ptrValue1, reflect.ValueOf(h.Conn)) } - ret := cmdlister.Call(params) + ret := cmdlister.Func.Call(params) if len(ret) <= 0 { //如果判断没有参数,那就说明这个包没有返回参数 return