diff --git a/README.md b/README.md index ab3a2efd0..7d5893059 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ go tool pprof -http :8081 "http://127.0.0.1:9909/debug/pprof/profile" go tool pprof -http :8081 "http://202.189.15.67:62672/debug/pprof/profile" go tool pprof -http :8081 "http://8.162.8.203:9909/debug/pprof//profile" +go tool pprof -http :8081 "http://8.162.23.87:9909/debug/pprof//profile" + 详情查看 [文档](./docs) - [战斗](./docs/battle.md) diff --git a/common/socket/ServerEvent.go b/common/socket/ServerEvent.go index b2820906e..a7345fada 100644 --- a/common/socket/ServerEvent.go +++ b/common/socket/ServerEvent.go @@ -19,6 +19,7 @@ import ( "github.com/gogf/gf/v2/frame/g" "github.com/gogf/gf/v2/os/gtime" "github.com/panjf2000/gnet/v2" + "github.com/valyala/bytebufferpool" ) func (s *Server) Boot(serverid, port uint16) error { @@ -305,21 +306,41 @@ func (s *Server) onevent(c gnet.Conn, v []byte) { } // XORDecryptU 优化后的异或解密:减少内存分配,支持复用缓冲区 +// XORDecryptU 基于bytebufferpool优化的异或解密函数 +// 保留原有接口,无侵入式优化,高频调用下大幅减少内存分配和GC func XORDecryptU(encryptedData []byte, key uint32) []byte { if len(encryptedData) == 0 { return []byte{} } - // 预分配密钥字节数组(可全局缓存,避免每次创建) - var keyBytes [4]byte // 栈上分配,比make更高效 + // 1. 栈上分配密钥字节数组(无GC压力,保留原优化) + var keyBytes [4]byte binary.BigEndian.PutUint32(keyBytes[:], key) keyLen := len(keyBytes) - // 复用输出缓冲区:如果需要极致性能,可传入外部缓冲区 - decrypted := make([]byte, len(encryptedData)) + // 2. 从bytebufferpool获取池化缓冲区(替代make分配) + buf := bytebufferpool.Get() + defer bytebufferpool.Put(buf) // 函数结束自动归还缓冲区到池 + + // 3. 调整缓冲区长度,匹配待解密数据(避免扩容) + buf.B = buf.B[:0] // 清空原有数据,保留底层数组 + if cap(buf.B) < len(encryptedData) { + // 若缓冲区容量不足,直接扩容(bytebufferpool会自动管理) + buf.B = make([]byte, len(encryptedData)) + } else { + // 容量足够,直接调整长度 + buf.B = buf.B[:len(encryptedData)] + } + + // 4. 核心异或解密逻辑(直接操作buf.B,无额外内存分配) + decrypted := buf.B for i, b := range encryptedData { decrypted[i] = b ^ keyBytes[i%keyLen] } - return decrypted + // 5. 拷贝结果(关键:避免返回池化缓冲区,防止被后续调用覆盖) + result := make([]byte, len(decrypted)) + copy(result, decrypted) + + return result } diff --git a/common/utils/sturc/field.go b/common/utils/sturc/field.go index 2bcd598bc..21e8541b5 100644 --- a/common/utils/sturc/field.go +++ b/common/utils/sturc/field.go @@ -6,6 +6,7 @@ import ( "fmt" "math" "reflect" + "unsafe" ) type Field struct { @@ -76,40 +77,60 @@ func (f *Field) Size(val reflect.Value, options *Options) int { return size } +// 预定义常用的order,避免重复判断 +var defaultOrder = binary.BigEndian + +// packVal 优化版:减少反射开销+优化内存拷贝+优雅错误处理 func (f *Field) packVal(buf []byte, val reflect.Value, length int, options *Options) (size int, err error) { + // 1. 预缓存order,避免重复判断 order := f.Order - if options.Order != nil { + if options != nil && options.Order != nil { order = options.Order } - if f.Ptr { - val = val.Elem() + if order == nil { + order = defaultOrder } + + // 2. 处理指针类型:提前解引用,避免后续重复操作 + if f.Ptr { + if !val.IsNil() { + val = val.Elem() + } else { + return 0, fmt.Errorf("field %s is nil pointer", f.Name) + } + } + + // 3. 预解析类型,避免重复Resolve typ := f.Type.Resolve(options) + kind := val.Kind() + + // 4. 扁平化分支逻辑,减少嵌套 switch typ { case Struct: return f.Fields.Pack(buf, val, options) + case Bool, Int8, Int16, Int32, Int64, Uint8, Uint16, Uint32, Uint64: size = typ.Size() + if len(buf) < size { + return 0, fmt.Errorf("buf size %d < required %d", len(buf), size) + } + var n uint64 - switch f.kind { + switch kind { case reflect.Bool: - if val.Bool() { - n = 1 - } else { - n = 0 - } + n = boolToUint64(val.Bool()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: n = uint64(val.Int()) - default: + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: n = val.Uint() + default: + return 0, fmt.Errorf("unsupported kind %s for numeric type %s", kind, typ) } + + // 扁平化数值写入逻辑 switch typ { case Bool: - if n != 0 { - buf[0] = 1 - } else { - buf[0] = 0 - } + buf[0] = byte(n) case Int8, Uint8: buf[0] = byte(n) case Int16, Uint16: @@ -117,33 +138,90 @@ func (f *Field) packVal(buf []byte, val reflect.Value, length int, options *Opti case Int32, Uint32: order.PutUint32(buf, uint32(n)) case Int64, Uint64: - order.PutUint64(buf, uint64(n)) + order.PutUint64(buf, n) } + case Float32, Float64: size = typ.Size() + if len(buf) < size { + return 0, fmt.Errorf("buf size %d < required %d", len(buf), size) + } + + if kind != reflect.Float32 && kind != reflect.Float64 { + return 0, fmt.Errorf("unsupported kind %s for float type %s", kind, typ) + } n := val.Float() + switch typ { case Float32: order.PutUint32(buf, math.Float32bits(float32(n))) case Float64: order.PutUint64(buf, math.Float64bits(n)) } + case String: - switch f.kind { + // 优化String类型:减少内存拷贝 + switch kind { case reflect.String: + s := val.String() + size = len(s) + if len(buf) < size { + return 0, fmt.Errorf("buf size %d < string length %d", len(buf), size) + } + // 用unsafe直接拷贝字符串到buf,避免[]byte(s)的内存分配 + copyStringToBuf(buf, s) + + case reflect.Slice: + if val.Type().Elem().Kind() != reflect.Uint8 { + return 0, fmt.Errorf("unsupported slice type %s for String field", val.Type()) + } size = val.Len() - copy(buf, []byte(val.String())) - default: - // TODO: handle kind != bytes here - size = val.Len() + if len(buf) < size { + return 0, fmt.Errorf("buf size %d < bytes length %d", len(buf), size) + } + // 直接拷贝字节切片,避免冗余操作 copy(buf, val.Bytes()) + + default: + return 0, fmt.Errorf("unsupported kind %s for String type", kind) } + case CustomType: - return val.Addr().Interface().(Custom).Pack(buf, options) + // 优化反射断言:提前检查类型,避免panic + if !val.CanAddr() { + return 0, fmt.Errorf("custom type %s cannot take address", val.Type()) + } + custom, ok := val.Addr().Interface().(Custom) + if !ok { + return 0, fmt.Errorf("type %s does not implement Custom interface", val.Type()) + } + return custom.Pack(buf, options) + default: - panic(fmt.Sprintf("no pack handler for type: %s", typ)) + // 替换panic为error,避免程序崩溃 + return 0, fmt.Errorf("no pack handler for type: %s", typ) } - return + + return size, nil +} + +// 辅助函数:bool转uint64,减少inline重复代码 +func boolToUint64(b bool) uint64 { + if b { + return 1 + } + return 0 +} + +// 辅助函数:unsafe拷贝字符串到buf,避免[]byte(s)的内存分配 +// 注意:仅在确定buf长度足够时使用 +func copyStringToBuf(buf []byte, s string) { + // unsafe转换:字符串转字节切片,无内存分配 + src := *(*[]byte)(unsafe.Pointer(&struct { + string + cap int + }{s, len(s)})) + copy(buf, src) } func (f *Field) Pack(buf []byte, val reflect.Value, length int, options *Options) (int, error) { diff --git a/modules/config/controller/admin/map.go b/modules/config/controller/admin/map.go index 79ff787fd..2fbcf31af 100644 --- a/modules/config/controller/admin/map.go +++ b/modules/config/controller/admin/map.go @@ -3,6 +3,9 @@ package admin import ( "blazing/cool" "blazing/modules/config/service" + "context" + + "github.com/gogf/gf/v2/frame/g" ) type MapController struct { @@ -20,3 +23,13 @@ func init() { }, }) } + +type TimeMapReq struct { + g.Meta `path:"/timemap" method:"POST"` +} + +func (this *MapController) TimeMap(ctx context.Context, req *TimeMapReq) (res *cool.BaseRes, err error) { + res = &cool.BaseRes{} + res.Data = service.NewMapService().GetTimeMap() + return res, nil +} diff --git a/modules/config/model/map.go b/modules/config/model/map.go index ecdcd0a61..9faed9266 100644 --- a/modules/config/model/map.go +++ b/modules/config/model/map.go @@ -16,6 +16,8 @@ type MapConfig struct { MapID uint32 `gorm:"not null;primaryKey;comment:'地图唯一ID(主键)'" json:"map_id" description:"地图ID"` WeatherType []uint32 `gorm:"type:int[];comment:'天气类型( 1-雨天,2-雪天)'" json:"weather_type"` + //是否超时空 + IsTimeSpace int `gorm:"type:int;default:0;comment:'是否超时空'" json:"is_time_space"` // 掉落物配置 DropItemIds []uint32 `gorm:"type:int[];comment:'掉落物IDs" json:"drop_item_ids"` diff --git a/modules/config/service/map.go b/modules/config/service/map.go index 31b39c486..efe885f96 100644 --- a/modules/config/service/map.go +++ b/modules/config/service/map.go @@ -29,3 +29,12 @@ func (s *MapService) GetData(p1 uint32) (ret *model.MapConfig) { return } +func (s *MapService) GetTimeMap() (ret []model.MapConfig) { + //cacheKey := strings.Join([]string{fmt.Sprintf("%d", p1), fmt.Sprintf("%d", p2)}, ":") + m := dbm_notenable(s.Model) + + m.Where(`is_time_space`, 1).Scan(&ret) + + return + +}