diff --git a/game/activity/activity.go b/game/activity/activity.go index 959f54a..7d7dc36 100644 --- a/game/activity/activity.go +++ b/game/activity/activity.go @@ -1,14 +1,11 @@ package activity import ( - "errors" "fmt" "github.com/kercylan98/minotaur/utils/generic" "github.com/kercylan98/minotaur/utils/timer" - "github.com/kercylan98/minotaur/utils/times" "reflect" "sync" - "sync/atomic" "time" ) @@ -25,18 +22,12 @@ var ( ticker *timer.Ticker tickerOnce sync.Once - isDebug atomic.Bool ) func init() { ticker = timer.GetTicker(10) } -// SetDebug 设置是否开启调试模式 -func SetDebug(d bool) { - isDebug.Store(d) -} - // SetTicker 设置自定义定时器,该方法必须在使用活动系统前调用,且只能调用一次 func SetTicker(size int, options ...timer.Option) { tickerOnce.Do(func() { @@ -47,22 +38,31 @@ func SetTicker(size int, options ...timer.Option) { }) } +// LoadGlobalData 加载所有活动全局数据 +func LoadGlobalData(handler func(activityType, activityId, data any)) { + for _, f := range activityGlobalDataLoader { + f(handler) + } +} + +// LoadEntityData 加载所有活动实体数据 +func LoadEntityData(handler func(activityType, activityId, entityId, data any)) { + for _, f := range activityEntityDataLoader { + f(handler) + } +} + // LoadOrRefreshActivity 加载或刷新活动 // - 通常在活动配置刷新时候将活动通过该方法注册或刷新 -func LoadOrRefreshActivity[Type, ID generic.Basic](activityType Type, activityId ID, options ...Option[Type, ID]) error { - register := getControllerRegister(activityType) - if register == nil { - return errors.New("activity type not registered") +func LoadOrRefreshActivity[Type, ID generic.Basic](activityType Type, activityId ID, options ...*Options) error { + register, exist := activityRegister[activityType] + if !exist { + return fmt.Errorf("activity type %v not registered, activity %v registration failed", activityType, activityId) } - act, initFinishCallback := register(activityType, activityId) - activity := act.(*Activity[Type, ID]) - for _, option := range options { - option(activity) - } - initFinishCallback(activity) - if !activity.tl.Check(true, stateLine...) { - return errors.New("activity state timeline is invalid") + activity := register(activityId, initOptions(options...)).(*Activity[Type, ID]) + if !activity.options.Tl.Check(true, stateLine...) { + return fmt.Errorf("activity %v state timeline is invalid", activityId) } stateTrigger := map[byte]func(){ @@ -71,14 +71,15 @@ func LoadOrRefreshActivity[Type, ID generic.Basic](activityType Type, activityId stateEnded: func() { OnEndedEvent(activity); OnExtendedShowStartedEvent(activity) }, stateExtendedShowEnded: func() { OnExtendedShowEndedEvent(activity) }, } + for _, state := range stateLine { - if activity.tl.HasState(state) { - activity.tl.AddTriggerToState(state, stateTrigger[state]) + if activity.options.Tl.HasState(state) { + activity.options.Tl.AddTriggerToState(state, stateTrigger[state]) continue } for next := state; next <= stateLine[len(stateLine)-1]; next++ { - if activity.tl.HasState(next) { - activity.tl.AddTriggerToState(next, stateTrigger[state]) + if activity.options.Tl.HasState(next) { + activity.options.Tl.AddTriggerToState(next, stateTrigger[state]) break } } @@ -88,69 +89,55 @@ func LoadOrRefreshActivity[Type, ID generic.Basic](activityType Type, activityId return nil } -// LoadGlobalData 加载所有活动全局数据 -// - 一般用于持久化活动数据 -func LoadGlobalData(handler func(activityType, activityId, data any)) { - for _, f := range controllerGlobalDataReader { - f(handler) - } -} - -// LoadEntityData 加载所有活动实体数据 -// - 一般用于持久化活动数据 -func LoadEntityData(handler func(activityType, activityId, entityId, data any)) { - for _, f := range controllerEntityDataReader { - f(handler) - } -} - // Activity 活动描述 type Activity[Type, ID generic.Basic] struct { - id ID // 活动 ID - t Type // 活动类型 - state byte // 活动状态 - tl *times.StateLine[byte] // 活动时间线 - loop time.Duration // 活动多久循环一次 - lazy bool // 是否懒加载 - tickerKey string // 定时器 key - retention time.Duration // 活动数据保留时间 - retentionKey string // 保留定时器 key + id ID // 活动 ID + t Type // 活动类型 + options *Options // 活动选项 + state byte // 活动状态 + + lazy bool // 是否懒加载 + tickerKey string // 定时器 key + retention time.Duration // 活动数据保留时间 + retentionKey string // 保留定时器 key mutex sync.RWMutex getLastNewDayTime func() time.Time // 获取上次新的一天的时间 setLastNewDayTime func(time.Time) // 设置上次新的一天的时间 + clearData func() // 清理活动数据 + initializeData func() // 初始化活动数据 } func (slf *Activity[Type, ID]) refresh() { slf.mutex.Lock() defer slf.mutex.Unlock() curr := time.Now() - if slf.state = slf.tl.GetStateByTime(curr); slf.state == stateUpcoming { + if slf.state = slf.options.Tl.GetStateByTime(curr); slf.state == stateUpcoming { ticker.StopTimer(slf.retentionKey) - resetActivityData(slf.t, slf.id) + slf.initializeData() } - for _, f := range slf.tl.GetTriggerByState(slf.state) { + for _, f := range slf.options.Tl.GetTriggerByState(slf.state) { if f != nil { f() } } - next := slf.tl.GetNextTimeByState(slf.state) + next := slf.options.Tl.GetNextTimeByState(slf.state) if !next.IsZero() && next.After(curr) { ticker.After(slf.tickerKey, next.Sub(curr)+time.Millisecond*100, slf.refresh) } else { ticker.StopTimer(slf.tickerKey) ticker.StopTimer(fmt.Sprintf("activity:new_day:%d:%v", reflect.ValueOf(slf.t).Kind(), slf.id)) - if slf.loop > 0 { - slf.tl.Move(slf.loop * 2) - ticker.After(slf.tickerKey, slf.loop+time.Millisecond*100, slf.refresh) + if slf.options.Loop > 0 { + slf.options.Tl.Move(slf.options.Loop * 2) + ticker.After(slf.tickerKey, slf.options.Loop+time.Millisecond*100, slf.refresh) return } if slf.retention > 0 { ticker.After(slf.tickerKey, slf.retention, func() { ticker.StopTimer(slf.retentionKey) - resetActivityData(slf.t, slf.id) + slf.clearData() }) } } diff --git a/game/activity/activity_test.go b/game/activity/activity_test.go index af551db..4ec4006 100644 --- a/game/activity/activity_test.go +++ b/game/activity/activity_test.go @@ -16,8 +16,9 @@ type PlayerData struct { } func TestRegTypeByGlobalData(t *testing.T) { - controller := activity.DefineGlobalDataActivity[int, int, *ActivityData](1, func(activityId int, data *activity.DataMeta[*ActivityData]) { - data.Data.players = append(data.Data.players, "temp") + + controller := activity.DefineGlobalDataActivity[int, int, *ActivityData](1).InitializeGlobalData(func(activityId int, data *activity.DataMeta[*ActivityData]) { + data.Data.players = []string{"1", "2", "3"} }) activity.RegUpcomingEvent(1, func(activityId int) { @@ -48,12 +49,12 @@ func TestRegTypeByGlobalData(t *testing.T) { now := time.Now() - if err := activity.LoadOrRefreshActivity(1, 1, - activity.WithUpcomingTime[int, int](now.Add(1*time.Second)), - activity.WithStartTime[int, int](now.Add(2*times.Second)), - activity.WithEndTime[int, int](now.Add(3*times.Second)), - activity.WithExtendedShowTime[int, int](now.Add(4*times.Second)), - activity.WithLoop[int, int](3*times.Second), + if err := activity.LoadOrRefreshActivity(1, 1, activity.NewOptions(). + WithUpcomingTime(now.Add(1*time.Second)). + WithStartTime(now.Add(2*times.Second)). + WithEndTime(now.Add(3*times.Second)). + WithExtendedShowTime(now.Add(4*times.Second)). + WithLoop(3*times.Second), ); err != nil { t.Fatal(err) } diff --git a/game/activity/controller.go b/game/activity/controller.go index 7231c13..bdbffb9 100644 --- a/game/activity/controller.go +++ b/game/activity/controller.go @@ -2,6 +2,7 @@ package activity import ( "github.com/kercylan98/minotaur/utils/generic" + "reflect" "sync" ) @@ -9,80 +10,79 @@ type none byte // DefineNoneDataActivity 声明无数据的活动类型 func DefineNoneDataActivity[Type, ID generic.Basic](activityType Type) NoneDataActivityController[Type, ID, none, none, none] { - return regController(activityType, &Controller[Type, ID, none, none, none]{ + return regController(&Controller[Type, ID, none, none, none]{ t: activityType, }) } // DefineGlobalDataActivity 声明拥有全局数据的活动类型 -func DefineGlobalDataActivity[Type, ID generic.Basic, Data any](activityType Type, initializer func(activityId ID, data *DataMeta[Data])) GlobalDataActivityController[Type, ID, Data, none, none] { - return regController(activityType, &Controller[Type, ID, Data, none, none]{ - t: activityType, - globalData: make(map[ID]*DataMeta[Data]), - globalInit: initializer, +func DefineGlobalDataActivity[Type, ID generic.Basic, Data any](activityType Type) GlobalDataActivityController[Type, ID, Data, none, none] { + return regController(&Controller[Type, ID, Data, none, none]{ + t: activityType, }) } // DefineEntityDataActivity 声明拥有实体数据的活动类型 -func DefineEntityDataActivity[Type, ID, EntityID generic.Basic, EntityData any](activityType Type, initializer func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) EntityDataActivityController[Type, ID, none, EntityID, EntityData] { - return regController(activityType, &Controller[Type, ID, none, EntityID, EntityData]{ - t: activityType, - entityData: make(map[ID]map[EntityID]*EntityDataMeta[EntityData]), - entityInit: initializer, +func DefineEntityDataActivity[Type, ID, EntityID generic.Basic, EntityData any](activityType Type) EntityDataActivityController[Type, ID, none, EntityID, EntityData] { + return regController(&Controller[Type, ID, none, EntityID, EntityData]{ + t: activityType, }) } // DefineGlobalAndEntityDataActivity 声明拥有全局数据和实体数据的活动类型 -func DefineGlobalAndEntityDataActivity[Type, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any](activityType Type, globalInitializer func(activityId ID, data *DataMeta[Data]), entityInitializer func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData] { - return regController(activityType, &Controller[Type, ID, Data, EntityID, EntityData]{ - t: activityType, - globalData: make(map[ID]*DataMeta[Data]), - entityData: make(map[ID]map[EntityID]*EntityDataMeta[EntityData]), - globalInit: globalInitializer, - entityInit: entityInitializer, +func DefineGlobalAndEntityDataActivity[Type, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any](activityType Type) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData] { + return regController(&Controller[Type, ID, Data, EntityID, EntityData]{ + t: activityType, }) } // Controller 活动控制器 type Controller[Type, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] struct { - t Type // 活动类型 - activities map[ID]*Activity[Type, ID] // 活动 - globalInit func(activityId ID, data *DataMeta[Data]) // 全局初始化器 - entityInit func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData]) // 实体初始化器 - globalDataLoader func(activityId any) // 全局数据加载器 - entityDataLoader func(activityId any, entityId any) // 实体数据加载器 - globalInitializer func(activityType Type, activityId ID, data map[ID]*DataMeta[Data]) // 全局数据初始化器 - entityInitializer func(activityType Type, activityId ID, data map[ID]*DataMeta[Data], entityData map[ID]map[EntityID]*EntityDataMeta[EntityData]) // 实体数据初始化器 - globalData map[ID]*DataMeta[Data] // 全局数据 - entityData map[ID]map[EntityID]*EntityDataMeta[EntityData] // 实体数据 - mutex sync.RWMutex + t Type // 活动类型 + activities map[ID]*Activity[Type, ID] // 活动列表 + globalData map[ID]*DataMeta[Data] // 全局数据 + entityData map[ID]map[EntityID]*EntityDataMeta[EntityData] // 实体数据 + entityTof reflect.Type // 实体数据类型 + globalInit func(activityId ID, data *DataMeta[Data]) // 全局数据初始化函数 + entityInit func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData]) // 实体数据初始化函数 + mutex sync.RWMutex } // GetGlobalData 获取特定活动全局数据 func (slf *Controller[Type, ID, Data, EntityID, EntityData]) GetGlobalData(activityId ID) Data { - slf.globalDataLoader(activityId) slf.mutex.RLock() defer slf.mutex.RUnlock() - return slf.globalData[activityId].Data + global := slf.globalData[activityId] + if slf.globalInit != nil { + global.once.Do(func() { + slf.globalInit(activityId, global) + }) + } + return global.Data } // GetEntityData 获取特定活动实体数据 func (slf *Controller[Type, ID, Data, EntityID, EntityData]) GetEntityData(activityId ID, entityId EntityID) EntityData { - slf.entityDataLoader(activityId, entityId) slf.mutex.RLock() defer slf.mutex.RUnlock() - return slf.entityData[activityId][entityId].Data -} - -// GetAllEntityData 获取特定活动所有实体数据 -func (slf *Controller[Type, ID, Data, EntityID, EntityData]) GetAllEntityData(activityId ID) map[EntityID]EntityData { - var entities = make(map[EntityID]EntityData) - slf.mutex.RLock() - for k, v := range slf.entityData[activityId] { - entities[k] = v.Data + entities, exist := slf.entityData[activityId] + if !exist { + entities = make(map[EntityID]*EntityDataMeta[EntityData]) + slf.entityData[activityId] = entities } - slf.mutex.RUnlock() - return entities + entity, exist := entities[entityId] + if !exist { + entity = &EntityDataMeta[EntityData]{ + Data: reflect.New(slf.entityTof).Interface().(EntityData), + } + entities[entityId] = entity + } + if slf.entityInit != nil { + entity.once.Do(func() { + slf.entityInit(activityId, entityId, entity) + }) + } + return entity.Data } // IsOpen 活动是否开启 @@ -108,7 +108,7 @@ func (slf *Controller[Type, ID, Data, EntityID, EntityData]) IsShow(activityId I } activity.mutex.RLock() defer activity.mutex.RUnlock() - return activity.state == stateUpcoming || (activity.state == stateEnded && activity.tl.HasState(stateExtendedShowEnded)) + return activity.state == stateUpcoming || (activity.state == stateEnded && activity.options.Tl.HasState(stateExtendedShowEnded)) } // IsOpenOrShow 活动是否开启或展示 @@ -122,7 +122,7 @@ func (slf *Controller[Type, ID, Data, EntityID, EntityData]) IsOpenOrShow(activi activity.mutex.RLock() defer activity.mutex.RUnlock() - return activity.state == stateStarted || activity.state == stateUpcoming || (activity.state == stateEnded && activity.tl.HasState(stateExtendedShowEnded)) + return activity.state == stateStarted || activity.state == stateUpcoming || (activity.state == stateEnded && activity.options.Tl.HasState(stateExtendedShowEnded)) } // Refresh 刷新活动 @@ -135,3 +135,24 @@ func (slf *Controller[Type, ID, Data, EntityID, EntityData]) Refresh(activityId } activity.refresh() } + +func (slf *Controller[Type, ID, Data, EntityID, EntityData]) InitializeNoneData(handler func(activityId ID, data *DataMeta[Data])) NoneDataActivityController[Type, ID, Data, EntityID, EntityData] { + slf.globalInit = handler + return slf +} + +func (slf *Controller[Type, ID, Data, EntityID, EntityData]) InitializeGlobalData(handler func(activityId ID, data *DataMeta[Data])) GlobalDataActivityController[Type, ID, Data, EntityID, EntityData] { + slf.globalInit = handler + return slf +} + +func (slf *Controller[Type, ID, Data, EntityID, EntityData]) InitializeEntityData(handler func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) EntityDataActivityController[Type, ID, Data, EntityID, EntityData] { + slf.entityInit = handler + return slf +} + +func (slf *Controller[Type, ID, Data, EntityID, EntityData]) InitializeGlobalAndEntityData(handler func(activityId ID, data *DataMeta[Data]), entityHandler func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData] { + slf.globalInit = handler + slf.entityInit = entityHandler + return slf +} diff --git a/game/activity/controller_interface.go b/game/activity/controller_interface.go index 7493af5..5b006b4 100644 --- a/game/activity/controller_interface.go +++ b/game/activity/controller_interface.go @@ -16,6 +16,11 @@ type BasicActivityController[Type, ID generic.Basic, Data any, EntityID generic. // NoneDataActivityController 无数据活动控制器 type NoneDataActivityController[Type, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any] interface { BasicActivityController[Type, ID, Data, EntityID, EntityData] + // InitializeNoneData 初始化活动 + // - 该函数提供了一个操作活动数据的入口,可以在该函数中对传入的活动数据进行初始化 + // + // 对于无数据活动,该函数的意义在于,可以在该函数中对活动进行初始化,比如设置活动的状态等,虽然为无数据活动,但是例如活动本身携带的状态数据也是需要加载的 + InitializeNoneData(handler func(activityId ID, data *DataMeta[Data])) NoneDataActivityController[Type, ID, Data, EntityID, EntityData] } // GlobalDataActivityController 全局数据活动控制器 @@ -23,6 +28,9 @@ type GlobalDataActivityController[Type, ID generic.Basic, Data any, EntityID gen BasicActivityController[Type, ID, Data, EntityID, EntityData] // GetGlobalData 获取全局数据 GetGlobalData(activityId ID) Data + // InitializeGlobalData 初始化活动 + // - 该函数提供了一个操作活动数据的入口,可以在该函数中对传入的活动数据进行初始化 + InitializeGlobalData(handler func(activityId ID, data *DataMeta[Data])) GlobalDataActivityController[Type, ID, Data, EntityID, EntityData] } // EntityDataActivityController 实体数据活动控制器 @@ -30,6 +38,9 @@ type EntityDataActivityController[Type, ID generic.Basic, Data any, EntityID gen BasicActivityController[Type, ID, Data, EntityID, EntityData] // GetEntityData 获取实体数据 GetEntityData(activityId ID, entityId EntityID) EntityData + // InitializeEntityData 初始化活动 + // - 该函数提供了一个操作活动数据的入口,可以在该函数中对传入的活动数据进行初始化 + InitializeEntityData(handler func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) EntityDataActivityController[Type, ID, Data, EntityID, EntityData] } // GlobalAndEntityDataActivityController 全局数据和实体数据活动控制器 @@ -39,4 +50,7 @@ type GlobalAndEntityDataActivityController[Type, ID generic.Basic, Data any, Ent GetGlobalData(activityId ID) Data // GetEntityData 获取实体数据 GetEntityData(activityId ID, entityId EntityID) EntityData + // InitializeGlobalAndEntityData 初始化活动 + // - 该函数提供了一个操作活动数据的入口,可以在该函数中对传入的活动数据进行初始化 + InitializeGlobalAndEntityData(handler func(activityId ID, data *DataMeta[Data]), entityHandler func(activityId ID, entityId EntityID, data *EntityDataMeta[EntityData])) GlobalAndEntityDataActivityController[Type, ID, Data, EntityID, EntityData] } diff --git a/game/activity/controller_internal.go b/game/activity/controller_internal.go index c459cd3..5ef7e17 100644 --- a/game/activity/controller_internal.go +++ b/game/activity/controller_internal.go @@ -3,155 +3,105 @@ package activity import ( "fmt" "github.com/kercylan98/minotaur/utils/generic" + "github.com/kercylan98/minotaur/utils/hash" "github.com/kercylan98/minotaur/utils/times" "reflect" - "sync" "time" ) var ( - controllers map[any]any // type -> controller (特定类型活动控制器) - controllerRegisters map[any]func(activityType, activityId any) (act any, optionInitCallback func(activity any)) // type -> register (控制活动注册到特定类型控制器的注册机) - controllerGlobalDataReader []func(handler func(activityType, activityId, data any)) // 活动全局数据读取器 - controllerEntityDataReader []func(handler func(activityType, activityId, entityId, data any)) // 活动实体数据读取器 - controllerReset map[any]func(activityId any) // type -> reset (活动数据重置器) - controllersLock sync.RWMutex + activityRegister map[any]func(activityId any, options *Options) (activity any) // type -> register (控制活动注册到特定类型控制器的注册机) + activityGlobalDataLoader []func(handler func(activityType, activityId, data any)) // 全局数据加载器 + activityEntityDataLoader []func(handler func(activityType, activityId, entityId, data any)) // 实体数据加载器 ) func init() { - controllers = make(map[any]any) - controllerRegisters = make(map[any]func(activityType, activityId any) (act any, optionInitCallback func(activity any))) - controllerGlobalDataReader = make([]func(handler func(activityType, activityId, data any)), 0) - controllerEntityDataReader = make([]func(handler func(activityType, activityId, entityId, data any)), 0) - controllerReset = make(map[any]func(activityId any)) + activityRegister = make(map[any]func(activityId any, options *Options) (activity any)) } -// regController 注册活动类型 -func regController[Type, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any](activityType Type, controller *Controller[Type, ID, Data, EntityID, EntityData]) *Controller[Type, ID, Data, EntityID, EntityData] { - controllersLock.Lock() - defer controllersLock.Unlock() +// regController 注册活动类型控制器 +func regController[Type, ID generic.Basic, Data any, EntityID generic.Basic, EntityData any](controller *Controller[Type, ID, Data, EntityID, EntityData]) *Controller[Type, ID, Data, EntityID, EntityData] { + var entityZero EntityData controller.activities = make(map[ID]*Activity[Type, ID]) - controllerGlobalDataReader = append(controllerGlobalDataReader, func(handler func(activityType, activityId, data any)) { - controller.mutex.RLock() - defer controller.mutex.RUnlock() - for activityId, data := range controller.globalData { - handler(activityType, activityId, data) - } - }) - controllerEntityDataReader = append(controllerEntityDataReader, func(handler func(activityType, activityId, entityId, data any)) { - controller.mutex.RLock() - defer controller.mutex.RUnlock() - for activityId, entities := range controller.entityData { - for entityId, data := range entities { - handler(activityType, activityId, entityId, data) - } - } - }) + controller.globalData = make(map[ID]*DataMeta[Data]) + controller.entityTof = reflect.TypeOf(entityZero) + if controller.entityTof.Kind() == reflect.Pointer { + controller.entityTof = controller.entityTof.Elem() + } - if controller.globalData != nil { - var d Data - var tof = reflect.TypeOf(d) - if tof.Kind() == reflect.Pointer { - tof = tof.Elem() - } - controller.globalDataLoader = func(activityId any) { - controller.mutex.Lock() - defer controller.mutex.Unlock() - var id = activityId.(ID) - if _, exist := controller.globalData[id]; exist { - return - } - data := &DataMeta[Data]{ - Data: reflect.New(tof).Interface().(Data), - } - if controller.globalInit != nil { - controller.globalInit(id, data) - } - controller.globalData[id] = data - } + // 反射类型 + var ( + zero Data + tof = reflect.TypeOf(zero) + ) + if tof.Kind() == reflect.Pointer { + tof = tof.Elem() } - if controller.entityData != nil { - var d Data - var tof = reflect.TypeOf(d) - if tof.Kind() == reflect.Pointer { - tof = tof.Elem() - } - controller.entityDataLoader = func(activityId any, entityId any) { - controller.mutex.Lock() - defer controller.mutex.Unlock() - var id, eid = activityId.(ID), entityId.(EntityID) - entities, exist := controller.entityData[id] - if !exist { - entities = make(map[EntityID]*EntityDataMeta[EntityData]) - controller.entityData[id] = entities - } - if _, exist = entities[eid]; exist { - return - } - data := &EntityDataMeta[EntityData]{ - Data: reflect.New(tof).Interface().(EntityData), - } - if controller.entityInit != nil { - controller.entityInit(id, eid, data) - } - entities[eid] = data - } - } - controllers[activityType] = controller - controllerRegisters[activityType] = func(activityType, activityId any) (act any, optionInitCallback func(activity any)) { - var at, ai = activityType.(Type), activityId.(ID) + + // 活动注册机(注册机内不加载活动数据,仅定义基本活动信息) + activityRegister[controller.t] = func(aid any, options *Options) any { + activityId := aid.(ID) controller.mutex.Lock() - activity, exist := controller.activities[ai] + activity, exist := controller.activities[activityId] if !exist { activity = &Activity[Type, ID]{ - t: at, - id: ai, - tl: times.NewStateLine[byte](stateClosed), - tickerKey: fmt.Sprintf("activity:%d:%v", reflect.ValueOf(at).Kind(), ai), + t: controller.t, + id: activityId, + options: options, + tickerKey: fmt.Sprintf("activity:%d:%v", reflect.ValueOf(controller.t).Kind(), activityId), getLastNewDayTime: func() time.Time { - return controller.globalData[ai].LastNewDay + return controller.globalData[activityId].LastNewDay }, setLastNewDayTime: func(t time.Time) { - controller.globalData[ai].LastNewDay = t + controller.globalData[activityId].LastNewDay = t + }, + clearData: func() { + controller.mutex.Lock() + defer controller.mutex.Unlock() + delete(controller.globalData, activityId) + delete(controller.entityData, activityId) + }, + initializeData: func() { + controller.mutex.Lock() + defer controller.mutex.Unlock() + controller.globalData[activityId] = &DataMeta[Data]{ + Data: reflect.New(tof).Interface().(Data), + } + if controller.entityData == nil { + controller.entityData = make(map[ID]map[EntityID]*EntityDataMeta[EntityData]) + } + controller.entityData[activityId] = make(map[EntityID]*EntityDataMeta[EntityData]) }, } + if activity.options == nil { + activity.options = NewOptions() + } + if activity.options.Tl == nil || activity.options.Tl.GetStateCount() == 0 { + activity.options.Tl = times.NewStateLine[byte](stateClosed) + } + controller.activities[activityId] = activity } controller.mutex.Unlock() - controller.activities[activity.id] = activity - return activity, func(activity any) { - act := activity.(*Activity[Type, ID]) - if !act.lazy { - controller.GetGlobalData(ai) + + // 全局数据加载器 + activityGlobalDataLoader = append(activityGlobalDataLoader, func(handler func(activityType any, activityId any, data any)) { + controller.mutex.RLock() + data := controller.globalData[activityId] + controller.mutex.RUnlock() + handler(controller.t, activityId, data) + }) + + // 实体数据加载器 + activityEntityDataLoader = append(activityEntityDataLoader, func(handler func(activityType any, activityId any, entityId any, data any)) { + controller.mutex.RLock() + entities := hash.Copy(controller.entityData[activityId]) + controller.mutex.RUnlock() + for entityId, data := range entities { + handler(controller.t, activityId, entityId, data) } - if act.retention > 0 { - act.retentionKey = fmt.Sprintf("%s:retention", act.tickerKey) - } - } - } - controllerReset[activityType] = func(activityId any) { - var id = activityId.(ID) - controller.mutex.Lock() - defer controller.mutex.Unlock() - delete(controller.globalData, id) - delete(controller.entityData, id) + }) + return activity } + return controller } - -// getControllerRegister 获取活动类型注册机 -func getControllerRegister[Type generic.Basic](activityType Type) func(activityType, activityId any) (act any, optionInitCallback func(activity any)) { - controllersLock.RLock() - defer controllersLock.RUnlock() - return controllerRegisters[activityType] -} - -// resetActivityData 重置活动数据 -func resetActivityData[Type, ID generic.Basic](activityType Type, activityId ID) { - controllersLock.RLock() - defer controllersLock.RUnlock() - reset, exist := controllerReset[activityType] - if !exist { - return - } - reset(activityId) -} diff --git a/game/activity/data.go b/game/activity/data.go index b3e7c18..2437e5b 100644 --- a/game/activity/data.go +++ b/game/activity/data.go @@ -1,19 +1,20 @@ package activity -import "time" +import ( + "sync" + "time" +) // DataMeta 全局活动数据 type DataMeta[Data any] struct { - Start time.Time `json:"start,omitempty"` // 活动开始时间 - End time.Time `json:"end,omitempty"` // 活动结束时间 + once sync.Once Data Data `json:"data,omitempty"` // 活动数据 LastNewDay time.Time `json:"lastNewDay,omitempty"` // 上次跨天时间 } // EntityDataMeta 活动实体数据 type EntityDataMeta[Data any] struct { - Start time.Time `json:"start,omitempty"` // 对象参与活动时间 - End time.Time `json:"end,omitempty"` // 对象结束活动时间 + once sync.Once Data Data `json:"data,omitempty"` // 活动数据 LastNewDay time.Time `json:"lastNewDay,omitempty"` // 上次跨天时间 } diff --git a/game/activity/options.go b/game/activity/options.go index 3eecb4d..7b18bed 100644 --- a/game/activity/options.go +++ b/game/activity/options.go @@ -1,56 +1,72 @@ package activity import ( - "github.com/kercylan98/minotaur/utils/generic" + "github.com/kercylan98/minotaur/utils/times" "time" ) -// Option 活动选项 -type Option[Type, ID generic.Basic] func(*Activity[Type, ID]) +// NewOptions 创建活动选项 +func NewOptions() *Options { + return new(Options) +} + +// initOptions 初始化活动选项 +func initOptions(opts ...*Options) *Options { + var opt *Options + if len(opts) > 0 { + opt = opts[0] + } + if opt == nil { + opt = NewOptions() + } + return opt +} + +// Options 活动选项 +type Options struct { + Tl *times.StateLine[byte] // 活动时间线 + Loop time.Duration // 活动循环,时间间隔小于等于 0 表示不循环 +} // WithUpcomingTime 设置活动预告时间 -func WithUpcomingTime[Type, ID generic.Basic](t time.Time) Option[Type, ID] { - return func(activity *Activity[Type, ID]) { - activity.tl.AddState(stateUpcoming, t) +func (slf *Options) WithUpcomingTime(t time.Time) *Options { + if slf.Tl == nil { + slf.Tl = times.NewStateLine[byte](stateClosed) } + slf.Tl.AddState(stateUpcoming, t) + return slf } // WithStartTime 设置活动开始时间 -func WithStartTime[Type, ID generic.Basic](t time.Time) Option[Type, ID] { - return func(activity *Activity[Type, ID]) { - activity.tl.AddState(stateStarted, t) +func (slf *Options) WithStartTime(t time.Time) *Options { + if slf.Tl == nil { + slf.Tl = times.NewStateLine[byte](stateClosed) } + slf.Tl.AddState(stateStarted, t) + return slf } // WithEndTime 设置活动结束时间 -func WithEndTime[Type, ID generic.Basic](t time.Time) Option[Type, ID] { - return func(activity *Activity[Type, ID]) { - activity.tl.AddState(stateEnded, t) +func (slf *Options) WithEndTime(t time.Time) *Options { + if slf.Tl == nil { + slf.Tl = times.NewStateLine[byte](stateClosed) } + slf.Tl.AddState(stateEnded, t) + return slf } // WithExtendedShowTime 设置延长展示时间 -func WithExtendedShowTime[Type, ID generic.Basic](t time.Time) Option[Type, ID] { - return func(activity *Activity[Type, ID]) { - activity.tl.AddState(stateExtendedShowEnded, t) +func (slf *Options) WithExtendedShowTime(t time.Time) *Options { + if slf.Tl == nil { + slf.Tl = times.NewStateLine[byte](stateClosed) } + slf.Tl.AddState(stateExtendedShowEnded, t) + return slf } // WithLoop 设置活动循环,时间间隔小于等于 0 表示不循环 // - 当活动状态展示结束后,会根据该选项设置的时间间隔重新开始 -func WithLoop[Type, ID generic.Basic](interval time.Duration) Option[Type, ID] { - return func(activity *Activity[Type, ID]) { - if interval <= 0 { - interval = 0 - } - activity.loop = interval - } -} - -// WithLazy 设置活动数据懒加载 -// - 该选项仅用于全局数据,默认情况下,活动全局数据会在活动注册时候加载,如果设置了该选项,则会在第一次获取数据时候加载 -func WithLazy[Type, ID generic.Basic](lazy bool) Option[Type, ID] { - return func(activity *Activity[Type, ID]) { - activity.lazy = lazy - } +func (slf *Options) WithLoop(interval time.Duration) *Options { + slf.Loop = interval + return slf }