Files
bl/logic/controller/Controller.go
昔念 79d4343cdc
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
```
feat(common/cool): 更新GetClient函数支持端口参数

更新GetClient函数签名以接收端口参数,并修改客户端映射键的计算方式,
添加GetClientOnly函数用于仅通过uid获取客户端。

fix(common/rpc): 修复RPC调用中的客户端获取方法

将GetClient调用替换为GetClientOnly,确保正确的客户端获取逻辑。

refactor(logic/controller): 重命名Port字段为UID并优化道具列表处理

将Controller结构体中的Port字段重命名为UID以更好地反映其用途,
优化GetUserItemList函数中道具列表的初始化和填充逻辑。

perf(logic): 调整性能分析web服务启动位置

将PprofWeb服务从全局启动移至调试模式下启动,优化服务配置。

refactor(logic/server): 更新服务器UID生成逻辑

修改Maincontroller的UID字段设置方式,使用服务器ID和端口组合生成唯一标识。

refactor(logic/service/player): 移除未使用的导入并优化怪物生成

移除未使用的service导入,优化怪物生成逻辑中的地图数据访问。

feat(logic/service/space): 添加PitS缓存映射并重构空间初始化

添加新的PitS字段
2026-03-02 23:59:15 +08:00

203 lines
6.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package controller 提供游戏逻辑的控制器层,负责处理各种游戏功能的请求分发。
// 包含用户、宠物、战斗、物品、地图、任务等各个模块的控制器方法。
// 通过反射和标签系统自动注册 cmd 处理方法,实现请求到处理函数的映射。
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
// Controller 分发cmd逻辑实现
type Controller struct {
UID uint32
RPCClient *struct {
Kick func(uint32) error
RegisterLogic func(uint32, uint32) error
}
}
// ParseCmd 将字节数组数据解析到指定类型的变量中
// 该函数使用struc库进行数据解包操作
// 参数 data: 需要解析的字节数据
// 返回值: 解析后的指定类型实例
func ParseCmd[T any](data []byte) T {
var result T
// 使用struc.Unpack将字节数据解包到result变量中
struc.Unpack(bytes.NewBuffer(data), &result)
return result
}
// Init 初始化控制器注册所有cmd处理方法
// 参数 isGame: 标识是否为游戏服务器(true)或登录服务器(false)
func Init(isGame bool) {
// 获取控制器实例的反射值
controllerValue := reflect.ValueOf(Maincontroller)
// 获取控制器类型
controllerType := controllerValue.Type()
// 遍历控制器的所有方法
for i := 0; i < controllerType.NumMethod(); i++ {
method := controllerType.Method(i)
methodValue := controllerValue.MethodByName(method.Name)
// 获取方法第一个参数的类型(请求结构体)
if methodValue.Type().NumIn() == 0 {
continue
}
// 解析请求结构体中的cmd标签
for _, cmd := range getCmd(methodValue.Type().In(0)) {
if cmd == 0 { // 说明不是有效的注册方法
glog.Warning(context.Background(), "方法参数必须包含CMD参数", method.Name, "跳过注册")
continue
}
// 根据服务器类型过滤cmd
// 登录服务器只处理小于1000的cmd
if !isGame && cmd > 1000 {
continue
}
// 游戏服务器只处理大于等于1000的cmd
if isGame && cmd < 1000 {
continue
}
// 注册命令处理函数
if cool.Config.ServerInfo.IsDebug != 0 {
fmt.Println("注册方法", cmd, method.Name)
}
cmdInfo := cool.Cmd{
Func: methodValue,
Req: methodValue.Type().In(0).Elem(),
// Res: , // TODO 待实现对不同用户初始化方法以取消全局cmdcache
}
// 获取req的实际类型如ReqLogin
reqType := reflect.TypeOf(cmdInfo.Req).Elem()
// 预编译创建req实例的函数返回结构体指针
cmdInfo.NewReqFunc = func() interface{} {
return reflect.New(reqType).Interface()
}
cool.CmdCache[cmd] = cmdInfo
// if exists { // 方法已存在
// glog.Error(context.Background(), "命令处理方法已存在,跳过注册", cmd, method.Name)
// }
}
}
}
var targetType = reflect.TypeOf(common.TomeeHeader{})
// 默认返回值(无匹配字段/解析失败时)
const defaultCmdValue = 0
// getCmd 从结构体类型中提取绑定的cmd指令递归查找嵌套结构体支持值/指针类型的TomeeHeader
// 参数 typ: 待解析的结构体类型(支持多层指针)
// 返回值: 解析到的cmd切片无匹配/解析失败时返回[defaultCmdValue]
func getCmd(typ reflect.Type) []uint32 {
// 递归解引用所有指针类型(处理 *struct、**struct 等场景)
for typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
// 非结构体类型直接返回默认值
if typ.Kind() != reflect.Struct {
return []uint32{defaultCmdValue}
}
// 遍历结构体字段查找TomeeHeader字段并解析cmd
for i := 0; i < typ.NumField(); i++ {
field := typ.Field(i)
// 尝试解析当前字段的cmd标签
cmdSlice, err := parseCmdTagWithStructField(field)
if err == nil { // 解析成功,直接返回结果
return cmdSlice
}
// 递归处理嵌套结构体(值/指针类型)
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
}
}
}
// 未找到目标字段/所有解析失败,返回默认值
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
}