perf: activity 包整体使用体验及性能优化,减少不必要的转换及反射,优化代码结构,优化可读性
This commit is contained in:
parent
9f60183cd2
commit
605a308d55
|
@ -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()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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"` // 上次跨天时间
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue