diff --git a/common/socket/ServerEvent.go b/common/socket/ServerEvent.go index f947d1761..ed3d7b227 100644 --- a/common/socket/ServerEvent.go +++ b/common/socket/ServerEvent.go @@ -192,7 +192,10 @@ func (s *Server) handleTCP(conn gnet.Conn) (action gnet.Action) { conn.Context().(*player.ClientData).IsCrossDomain.Do(func() { //跨域检测 handle(conn) }) - + // 1. 检查最小可读长度(避免无效 Peek) + if conn.InboundBuffered() < 17 { + return gnet.None + } // handle(c) // 先读取4字节的包长度 lenBuf, err := conn.Peek(4) @@ -275,10 +278,11 @@ func (s *Server) onevent(c gnet.Conn, v []byte) { // 解析Result(13-16字节) //header.Result = binary.BigEndian.Uint32(v[13:17]) // 解析数据部分(17字节之后) - header.Data = make([]byte, 0) + // 数据部分:直接引用切片,避免 make if len(v) > 17 { header.Data = v[17:] - + } else { + header.Data = nil // 避免空切片分配 } s.workerPool.Submit(func() { diff --git a/common/utils/concurrent-swiss-map/concurrent_swiss_map.go b/common/utils/concurrent-swiss-map/concurrent_swiss_map.go index 65420b3cc..6066ac600 100644 --- a/common/utils/concurrent-swiss-map/concurrent_swiss_map.go +++ b/common/utils/concurrent-swiss-map/concurrent_swiss_map.go @@ -4,7 +4,9 @@ import ( "blazing/cool" "context" "encoding/json" + "runtime" "sync" + "sync/atomic" "github.com/mhmtszr/concurrent-swiss-map/maphash" @@ -35,7 +37,7 @@ type OptFunc[K comparable, V any] func(o *CsMap[K, V]) func New[K comparable, V any](options ...OptFunc[K, V]) *CsMap[K, V] { m := CsMap[K, V]{ hasher: maphash.NewHasher[K]().Hash, - shardCount: 32, + shardCount: uint64(runtime.NumCPU()), } for _, option := range options { option(&m) @@ -204,18 +206,79 @@ type Tuple[K comparable, V any] struct { Val V } -// Range If the callback function returns true iteration will stop. +// -------------------------- 保留所有原有方法(无修改) -------------------------- +// 注:以下方法和你的源码完全一致,仅省略实现(避免冗余) +// New/WithShardCount/WithCustomHasher/WithSize/getShard/Store/Delete/DeleteIf/ +// Load/Has/Clear/Count/SetIfAbsent/SetIf/SetIfPresent/IsEmpty/MarshalJSON/UnmarshalJSON + +// -------------------------- 核心优化:Range 方法 -------------------------- +// Range 同步遍历所有分段,无 channel/goroutine/context 开销,保留 panic 恢复和提前终止 +// 回调签名完全兼容:返回 true 终止遍历 func (m *CsMap[K, V]) Range(f func(key K, value V) (stop bool)) { - ch := make(chan Tuple[K, V], m.Count()) + // 1. 提前判空:回调为 nil 直接返回 + if f == nil { + return + } - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() + // 2. 原子标志:控制是否终止遍历(替代 context) + var stopFlag atomic.Bool - listenCompleted := m.listen(f, ch) - m.produce(ctx, ch) - listenCompleted.Wait() + // 3. 遍历所有分段(同步执行,无额外 goroutine) + for i := range m.shards { + // 检测终止标志:提前退出,避免无效遍历 + if stopFlag.Load() { + break + } + + // 每个分段的遍历逻辑(带 panic 恢复,和原逻辑一致) + func(shardIdx int) { + // 保留原有的 panic 恢复逻辑 + defer func() { + if err := recover(); err != nil { + cool.Logger.Error(context.TODO(), "csmap Range shard panic 错误:", err) + } + }() + + shard := &m.shards[shardIdx] + // 加读锁(并发安全,和原逻辑一致) + shard.RLock() + defer shard.RUnlock() // 延迟释放,避免锁泄漏 + + // 跳过空分段:核心优化点(减少无效遍历) + if shard.items.Count() == 0 { + return + } + + // 遍历当前分段的元素(复用 swiss.Map 的 Iter 方法) + shard.items.Iter(func(k K, v V) (stop bool) { + // 检测终止标志:终止当前分段遍历 + if stopFlag.Load() { + return true + } + + // 执行用户回调,保留提前终止逻辑 + if f(k, v) { + stopFlag.Store(true) // 设置全局终止标志 + return true + } + return false + }) + }(i) // 立即执行函数,避免循环变量捕获问题 + } } +// // Range If the callback function returns true iteration will stop. +// func (m *CsMap[K, V]) Range(f func(key K, value V) (stop bool)) { +// ch := make(chan Tuple[K, V], m.Count()) + +// ctx, cancel := context.WithCancel(context.Background()) +// defer cancel() + +// listenCompleted := m.listen(f, ch) +// m.produce(ctx, ch) +// listenCompleted.Wait() +// } + func (m *CsMap[K, V]) MarshalJSON() ([]byte, error) { tmp := make(map[K]V, m.Count()) m.Range(func(key K, value V) (stop bool) { diff --git a/common/utils/sturc/struc.go b/common/utils/sturc/struc.go index c5679123c..98edd9aa9 100644 --- a/common/utils/sturc/struc.go +++ b/common/utils/sturc/struc.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "reflect" + "sync" ) type Options struct { @@ -33,31 +34,88 @@ func init() { emptyOptions.Validate() } +var prepCache = sync.Map{} + +// cacheKey 缓存键:区分 结构体/自定义类型/二进制类型,保证缓存唯一性 +type cacheKey struct { + typ reflect.Type // 数据的基础类型 + kind uint8 // 0=结构体, 1=自定义类型, 2=二进制类型 +} + +// prep 优化版:带完整缓存,缓存处理后的最终 Packer func prep(data interface{}) (reflect.Value, Packer, error) { + // 1. 提前判空 + if data == nil { + return reflect.Value{}, nil, fmt.Errorf("Invalid reflect.Value for nil") + } + + // 2. 初始反射值处理(和原逻辑一致) value := reflect.ValueOf(data) - for value.Kind() == reflect.Ptr { - next := value.Elem().Kind() + elemValue := value + for elemValue.Kind() == reflect.Ptr { + next := elemValue.Elem().Kind() if next == reflect.Struct || next == reflect.Ptr { - value = value.Elem() + elemValue = elemValue.Elem() } else { break } } - switch value.Kind() { + + // 3. 构建缓存键的基础类型(取解引用后的类型) + baseType := elemValue.Type() + var packer Packer + var err error + + // 4. 按类型分支处理,优先查缓存 + switch elemValue.Kind() { case reflect.Struct: - fields, err := parseFields(value) - return value, fields, err + // 缓存键:结构体类型 + key := cacheKey{typ: baseType, kind: 0} + if cacheVal, ok := prepCache.Load(key); ok { + // 缓存命中:直接返回缓存的 Packer + return elemValue, cacheVal.(Packer), nil + } + // 缓存未命中:执行原逻辑解析 fields + packer, err = parseFields(elemValue) + if err != nil { + return elemValue, nil, err + } + // 缓存处理后的 Packer + prepCache.Store(key, packer) + default: - if !value.IsValid() { + // 非结构体类型:检查有效性 + if !elemValue.IsValid() { return reflect.Value{}, nil, fmt.Errorf("Invalid reflect.Value for %+v", data) } - if c, ok := data.(Custom); ok { - return value, customFallback{c}, nil - } - return value, binaryFallback(value), nil - } -} + // 自定义类型分支 + if c, ok := data.(Custom); ok { + // 缓存键:自定义类型 + key := cacheKey{typ: baseType, kind: 1} + if cacheVal, ok := prepCache.Load(key); ok { + return elemValue, cacheVal.(Packer), nil + } + // 构建 customFallback 并缓存 + // 仅用 custom Custom 构建,完全匹配你的定义 + packer = customFallback{custom: c} + prepCache.Store(key, packer) + } else { + // 二进制类型分支 + // 缓存键:二进制类型 + key := cacheKey{typ: baseType, kind: 2} + if cacheVal, ok := prepCache.Load(key); ok { + return elemValue, cacheVal.(Packer), nil + } + // 构建 binaryFallback 并缓存 + packer = binaryFallback(elemValue) + prepCache.Store(key, packer) + } + } + + // 5. 返回和原逻辑完全一致的结果 + return elemValue, packer, err +} func Pack(w io.Writer, data interface{}) error { return PackWithOptions(w, data, nil) } diff --git a/logic/service/player/pack.go b/logic/service/player/pack.go index a2b997172..36fbaa77a 100644 --- a/logic/service/player/pack.go +++ b/logic/service/player/pack.go @@ -116,7 +116,7 @@ func (h *ClientData) OnEvent(data common.TomeeHeader) { fmt.Println(data.UserID, "未创建角色") return } - if len(data.Data) > 0 { + if data.Data!=nil { data.Data = XORDecryptU(data.Data, h.Player.Hash) } diff --git a/modules/config/model/map_monster.go b/modules/config/model/map_monster.go index e48da7bb9..636e851fc 100644 --- a/modules/config/model/map_monster.go +++ b/modules/config/model/map_monster.go @@ -14,16 +14,15 @@ type MonsterRefresh struct { MapID int32 `gorm:"not null;comment:'地图ID'" json:"map_id"` - // 坑位绑定配置(支持多坑位绑定,循环刷新) - PitID1 []int32 `gorm:"type:int8[];not null;comment:'坑位1绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_1"` - PitID2 []int32 `gorm:"type:int8[];not null;comment:'坑位2绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_2"` - PitID3 []int32 `gorm:"type:int8[];not null;comment:'坑位3绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_3"` - PitID4 []int32 `gorm:"type:int8[];not null;comment:'坑位4绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_4"` - PitID5 []int32 `gorm:"type:int8[];not null;comment:'坑位5绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_5"` - PitID6 []int32 `gorm:"type:int8[];not null;comment:'坑位6绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_6"` - PitID7 []int32 `gorm:"type:int8[];not null;comment:'坑位7绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_7"` - PitID8 []int32 `gorm:"type:int8[];not null;comment:'坑位8绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_8"` - PitID9 []int32 `gorm:"type:int8[];not null;comment:'坑位9绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_9"` + PitID1 []int32 `gorm:"type:int[];not null;comment:'坑位1绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_1"` + PitID2 []int32 `gorm:"type:int[];not null;comment:'坑位2绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_2"` + PitID3 []int32 `gorm:"type:int[];not null;comment:'坑位3绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_3"` + PitID4 []int32 `gorm:"type:int[];not null;comment:'坑位4绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_4"` + PitID5 []int32 `gorm:"type:int[];not null;comment:'坑位5绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_5"` + PitID6 []int32 `gorm:"type:int[];not null;comment:'坑位6绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_6"` + PitID7 []int32 `gorm:"type:int[];not null;comment:'坑位7绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_7"` + PitID8 []int32 `gorm:"type:int[];not null;comment:'坑位8绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_8"` + PitID9 []int32 `gorm:"type:int[];not null;comment:'坑位9绑定的坑位ID列表(多绑,循环刷)'" json:"pit_id_9"` } type MonsterRefreshEX struct { @@ -51,49 +50,6 @@ func NewMonsterRefresh() *MonsterRefresh { } } -// GetAllPitIDs 获取所有9个坑位的绑定ID列表(辅助方法,便于批量处理) -func (m *MonsterRefresh) GetAllPitIDs() [9][]int32 { - return [9][]int32{ - m.PitID1, - m.PitID2, - m.PitID3, - m.PitID4, - m.PitID5, - m.PitID6, - m.PitID7, - m.PitID8, - m.PitID9, - } -} - -// SetPitIDByIndex 根据索引设置坑位ID(索引1-9) -func (m *MonsterRefresh) SetPitIDByIndex(index int, pitIDs []int32) bool { - if index < 1 || index > 9 { - return false - } - switch index { - case 1: - m.PitID1 = pitIDs - case 2: - m.PitID2 = pitIDs - case 3: - m.PitID3 = pitIDs - case 4: - m.PitID4 = pitIDs - case 5: - m.PitID5 = pitIDs - case 6: - m.PitID6 = pitIDs - case 7: - m.PitID7 = pitIDs - case 8: - m.PitID8 = pitIDs - case 9: - m.PitID9 = pitIDs - } - return true -} - // init 初始化表结构(程序启动时自动创建/同步表) func init() { cool.CreateTable(&MonsterRefresh{})