fix(fight): 修正空变更导致的潜在逻辑问题
This commit is contained in:
@@ -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() //时间轮
|
||||
|
||||
@@ -49,7 +49,7 @@ var s = `
|
||||
|
||||
func Test_main(t *testing.T) {
|
||||
|
||||
initfile()
|
||||
// initfile()
|
||||
fmt.Println(SkillMap[10073])
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user