refactor: 重构游戏活动实现
This commit is contained in:
parent
da6cef4126
commit
390e8e75ef
|
@ -0,0 +1,77 @@
|
||||||
|
package activity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/utils/hash"
|
||||||
|
"github.com/kercylan98/minotaur/utils/offset"
|
||||||
|
"github.com/kercylan98/minotaur/utils/timer"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
TickerMark = "ACTIVITY_SYSTEM_TICKER"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
tickerStart = "MINOTAUR_ACTIVITY_%d_START_c8JDh23!df"
|
||||||
|
tickerStop = "MINOTAUR_ACTIVITY_%d_STOP_3d!fj2@#d"
|
||||||
|
tickerShowStart = "MINOTAUR_ACTIVITY_%d_SHOW_START_3d#f@2@#d"
|
||||||
|
tickerShowStop = "MINOTAUR_ACTIVITY_%d_SHOW_STOP_68!fj11#d"
|
||||||
|
tickerNewDay = "MINOTAUR_ACTIVITY_%d_NEW_DAY_3d!fd8@_d"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ticker *timer.Ticker
|
||||||
|
activityOffset *offset.Time
|
||||||
|
activities map[int64]activityInterface
|
||||||
|
)
|
||||||
|
|
||||||
|
// NoneData 空数据活动
|
||||||
|
type NoneData struct{}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SetTicker(nil)
|
||||||
|
activityOffset = offset.GetGlobal()
|
||||||
|
activities = map[int64]activityInterface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register 注册活动
|
||||||
|
func Register[PlayerID comparable, ActivityData, PlayerData any](activity *Activity[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
activity.Register()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get 获取活动
|
||||||
|
func Get[PlayerID comparable, ActivityData, PlayerData any](activityId int64) *Activity[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return activities[activityId].(*Activity[PlayerID, ActivityData, PlayerData])
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasActivity 检查活动是否存在
|
||||||
|
func HasActivity(activityId int64) bool {
|
||||||
|
return hash.Exist(activities, activityId)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOffsetTime 设置活动的时间偏移时间
|
||||||
|
func SetOffsetTime(time *offset.Time) {
|
||||||
|
activityOffset = time
|
||||||
|
for _, activity := range activities {
|
||||||
|
activity.stopTicker()
|
||||||
|
activity.refreshTicker(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTicker 通过指定特定的定时器取代默认的定时器
|
||||||
|
// - 当传入的定时器为 nil 时,将使用默认的定时器
|
||||||
|
func SetTicker(t *timer.Ticker) {
|
||||||
|
if ticker != nil {
|
||||||
|
if ticker.Mark() == TickerMark {
|
||||||
|
ticker.Release()
|
||||||
|
}
|
||||||
|
for _, activity := range activities {
|
||||||
|
activity.stopTicker()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ticker = t; ticker == nil {
|
||||||
|
ticker = timer.GetTicker(30, timer.WithMark(TickerMark))
|
||||||
|
for _, activity := range activities {
|
||||||
|
activity.refreshTicker(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,222 +2,327 @@ package activity
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kercylan98/minotaur/utils/hash"
|
"github.com/kercylan98/minotaur/utils/generic"
|
||||||
"github.com/kercylan98/minotaur/utils/offset"
|
|
||||||
"github.com/kercylan98/minotaur/utils/timer"
|
|
||||||
"github.com/kercylan98/minotaur/utils/times"
|
"github.com/kercylan98/minotaur/utils/times"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New[PlayerID comparable, Data any, PlayerData any](id int64, startTime, endTime time.Time, data Data, generatePlayerDataHandle func() PlayerData, options ...Option[PlayerID, Data, PlayerData]) *Activity[PlayerID, Data, PlayerData] {
|
// NewActivity 创建一个新的活动
|
||||||
activity := &Activity[PlayerID, Data, PlayerData]{
|
// - id: 活动 ID
|
||||||
|
// - period: 活动周期
|
||||||
|
func NewActivity[PlayerID comparable, ActivityData, PlayerData any](id int64, period times.Period, options ...Option[PlayerID, ActivityData, PlayerData]) *Activity[PlayerID, ActivityData, PlayerData] {
|
||||||
|
activity := &Activity[PlayerID, ActivityData, PlayerData]{
|
||||||
id: id,
|
id: id,
|
||||||
playerData: make(map[PlayerID]PlayerData),
|
data: newData[PlayerID, ActivityData, PlayerData](),
|
||||||
newDay: map[PlayerID]int64{},
|
period: period,
|
||||||
online: map[PlayerID]struct{}{},
|
|
||||||
data: data,
|
|
||||||
generatePlayerDataHandle: generatePlayerDataHandle,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, option := range options {
|
for _, option := range options {
|
||||||
option(activity)
|
option(activity)
|
||||||
}
|
}
|
||||||
if activity.offset == nil {
|
|
||||||
activity.offset = offset.NewTime(0)
|
|
||||||
}
|
|
||||||
if activity.ticker == nil {
|
|
||||||
activity.ticker = timer.GetTicker(5)
|
|
||||||
}
|
|
||||||
activity.SetTime(startTime, endTime)
|
|
||||||
return activity
|
return activity
|
||||||
}
|
}
|
||||||
|
|
||||||
type Activity[PlayerID comparable, Data any, PlayerData any] struct {
|
// Activity 活动
|
||||||
|
type Activity[PlayerID comparable, ActivityData, PlayerData any] struct {
|
||||||
id int64
|
id int64
|
||||||
ticker *timer.Ticker
|
data *Data[PlayerID, ActivityData, PlayerData]
|
||||||
offset *offset.Time
|
period times.Period
|
||||||
startTime time.Time
|
beforeShow, afterShow time.Time
|
||||||
endTime time.Time
|
|
||||||
data Data
|
|
||||||
playerData map[PlayerID]PlayerData
|
|
||||||
newDay map[PlayerID]int64
|
|
||||||
online map[PlayerID]struct{}
|
|
||||||
generatePlayerDataHandle func() PlayerData
|
|
||||||
|
|
||||||
startEventHandles []StartEventHandle[PlayerID, Data, PlayerData]
|
playerDataLoadHandle func(activity *Activity[PlayerID, ActivityData, PlayerData], playerId PlayerID) PlayerData
|
||||||
finishEventHandles []FinishEventHandle[PlayerID, Data, PlayerData]
|
|
||||||
newDayEventHandles []NewDayEventHandle[PlayerID, Data, PlayerData]
|
startEventHandles []StartEventHandle[PlayerID, ActivityData, PlayerData]
|
||||||
playerNewDayEventHandles []PlayerNewDayEventHandle[PlayerID, Data, PlayerData]
|
stopEventHandles []StopEventHandle[PlayerID, ActivityData, PlayerData]
|
||||||
|
startShowEventHandles []StartShowEventHandle[PlayerID, ActivityData, PlayerData]
|
||||||
|
stopShowEventHandles []StopShowEventHandle[PlayerID, ActivityData, PlayerData]
|
||||||
|
playerJoinEventHandles []PlayerJoinEventHandle[PlayerID, ActivityData, PlayerData]
|
||||||
|
playerLeaveEventHandles []PlayerLeaveEventHandle[PlayerID, ActivityData, PlayerData]
|
||||||
|
newDayEventHandles []NewDayEventHandle[PlayerID, ActivityData, PlayerData]
|
||||||
|
playerNewDayEventHandles []PlayerNewDayEventHandle[PlayerID, ActivityData, PlayerData]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetId 获取活动 ID
|
// GetStart 获取活动开始时间
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetId() int64 {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) GetStart() time.Time {
|
||||||
return slf.id
|
return slf.period.Start()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetEnd 获取活动结束时间
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) GetEnd() time.Time {
|
||||||
|
return slf.period.End()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPeriod 获取活动周期
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) GetPeriod() times.Period {
|
||||||
|
return slf.period
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreData 通过特定的存储函数存储活动数据
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) StoreData(handle func(data *Data[PlayerID, ActivityData, PlayerData])) {
|
||||||
|
handle(slf.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetActivityData 获取活动数据
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) GetActivityData() ActivityData {
|
||||||
|
return slf.data.Data
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPlayerData 获取玩家数据
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) GetPlayerData(playerId PlayerID) PlayerData {
|
||||||
|
return slf.data.PlayerData[playerId]
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeTime 修改活动时间
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) ChangeTime(period times.Period) {
|
||||||
|
slf.period = period
|
||||||
|
slf.refreshTicker(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeBeforeShowTime 修改活动开始前的展示时间
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) ChangeBeforeShowTime(beforeShow time.Time) {
|
||||||
|
slf.beforeShow = beforeShow
|
||||||
|
slf.refreshTicker(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChangeAfterShowTime 修改活动结束后的展示时间
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) ChangeAfterShowTime(afterShow time.Time) {
|
||||||
|
slf.afterShow = afterShow
|
||||||
|
slf.refreshTicker(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsShowAndOpen 判断活动是否展示并开启
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) IsShowAndOpen() bool {
|
||||||
|
return slf.IsShow() && slf.IsOpen()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsShow 判断活动是否展示
|
||||||
|
// - 活动展示时,活动并非一定开启。常用于活动的预告和结束后的成果展示
|
||||||
|
// - 当活动没有设置展示时间时,活动展示与活动开启一致
|
||||||
|
// - 如果活动仅设置了活动开始前的展示时间,则活动展示将从开始前的展示时间持续到活动结束
|
||||||
|
// - 如果活动仅设置了活动结束后的展示时间,则活动展示将从活动开始持续到结束后的展示时间
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) IsShow() bool {
|
||||||
|
if slf.beforeShow.IsZero() && slf.afterShow.IsZero() {
|
||||||
|
return slf.IsOpen()
|
||||||
|
} else if slf.beforeShow.IsZero() {
|
||||||
|
return slf.IsOpen() && slf.afterShow.After(activityOffset.Now())
|
||||||
|
} else if slf.afterShow.IsZero() {
|
||||||
|
return slf.IsOpen() && slf.beforeShow.Before(activityOffset.Now())
|
||||||
|
}
|
||||||
|
return times.NewPeriod(slf.beforeShow, slf.afterShow).IsOngoing(activityOffset.Now())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsOpen 判断活动是否开启
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) IsOpen() bool {
|
||||||
|
current := activityOffset.Now()
|
||||||
|
return slf.period.IsOngoing(current)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalid 判断活动是否无效
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) IsInvalid() bool {
|
||||||
|
current := activityOffset.Now()
|
||||||
|
if slf.beforeShow.IsZero() && !slf.afterShow.IsZero() {
|
||||||
|
return current.After(slf.afterShow) || current.Equal(slf.afterShow)
|
||||||
|
} else if !slf.beforeShow.IsZero() && slf.afterShow.IsZero() {
|
||||||
|
end := slf.GetEnd()
|
||||||
|
return current.After(end) || current.Equal(end)
|
||||||
|
} else if !slf.beforeShow.IsZero() && !slf.afterShow.IsZero() {
|
||||||
|
end := slf.GetEnd()
|
||||||
|
return current.After(end) || current.Equal(end)
|
||||||
|
} else {
|
||||||
|
end := slf.GetEnd()
|
||||||
|
return current.After(end) || current.Equal(end)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register 将该活动进行注册
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) Register() {
|
||||||
|
if slf.IsInvalid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !generic.IsNil(slf.data) {
|
||||||
|
activities[slf.id] = slf
|
||||||
|
}
|
||||||
|
slf.refreshTicker(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join 设置玩家加入活动
|
// Join 设置玩家加入活动
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) Join(playerId PlayerID) {
|
// - 通常玩家应该在上线的时候加入活动
|
||||||
if !hash.Exist(slf.playerData, playerId) {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) Join(playerId PlayerID) {
|
||||||
slf.playerData[playerId] = slf.generatePlayerDataHandle()
|
if slf.playerDataLoadHandle != nil {
|
||||||
}
|
playerData := slf.playerDataLoadHandle(slf, playerId)
|
||||||
if slf.IsFirstDay() || !hash.Exist(slf.online, playerId) {
|
if !generic.IsNil(playerData) {
|
||||||
return
|
slf.data.PlayerData[playerId] = playerData
|
||||||
}
|
|
||||||
nd := slf.newDay[playerId]
|
|
||||||
if nd > 0 {
|
|
||||||
ndt := time.Unix(nd, 0)
|
|
||||||
now := slf.offset.Now()
|
|
||||||
if !times.IsSameDay(ndt, now) {
|
|
||||||
slf.onPlayerNewDayEvent(playerId)
|
|
||||||
slf.newDay[playerId] = now.Unix()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
current := activityOffset.Now()
|
||||||
|
slf.OnPlayerJoinEvent(playerId)
|
||||||
|
slf.playerNewDay(playerId, current)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Leave 设置玩家离开活动
|
// Leave 设置玩家离开活动
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) Leave(playerId PlayerID) {
|
// - 当玩家离开活动时,玩家的活动数据将会被清除
|
||||||
delete(slf.online, playerId)
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) Leave(playerId PlayerID) {
|
||||||
|
slf.OnPlayerLeaveEvent(playerId)
|
||||||
|
delete(slf.data.PlayerData, playerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTime 设置活动开始时间和结束时间
|
// RegStartEvent 注册活动开始事件
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) SetTime(startTime, endTime time.Time) {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) RegStartEvent(handle StartEventHandle[PlayerID, ActivityData, PlayerData]) {
|
||||||
// 如果结束时间小于开始时间
|
|
||||||
if endTime.Before(startTime) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
slf.startTime, slf.endTime = startTime, endTime
|
|
||||||
if !slf.IsOpen() {
|
|
||||||
slf.ticker.StopTimer(fmt.Sprintf("ACTIVITY_%d_END", slf.id))
|
|
||||||
slf.ticker.StopTimer(fmt.Sprintf("ACTIVITY_%d_NEW_DAY", slf.id))
|
|
||||||
} else {
|
|
||||||
slf.ticker.After(fmt.Sprintf("ACTIVITY_%d_END", slf.id), slf.endTime.Sub(slf.startTime), func() {
|
|
||||||
slf.onFinishEvent()
|
|
||||||
})
|
|
||||||
now := slf.offset.Now()
|
|
||||||
next := times.GetToday(now).AddDate(0, 0, 1).Sub(now)
|
|
||||||
slf.nd(next)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsFirstDay 检查是否是活动第一天
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) IsFirstDay() bool {
|
|
||||||
return times.IsSameDay(slf.startTime, slf.offset.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsLastDay 检查是否是活动最后一天
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) IsLastDay() bool {
|
|
||||||
return times.IsSameDay(slf.endTime, slf.offset.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsOpen 检查活动是否开放
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) IsOpen() bool {
|
|
||||||
now := slf.offset.Now()
|
|
||||||
return now.After(slf.startTime) && now.Before(slf.endTime)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetData 获取活动数据
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetData() Data {
|
|
||||||
return slf.data
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPlayerData 获取活动玩家数据
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetPlayerData(playerId PlayerID) PlayerData {
|
|
||||||
return slf.playerData[playerId]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetLastNewDayTime 获取玩家最后触发新一天的时间
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetLastNewDayTime(playerId PlayerID) int64 {
|
|
||||||
return slf.newDay[playerId]
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllLastNewDayTime 获取所有玩家最后触发新一天的时间
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetAllLastNewDayTime() map[PlayerID]int64 {
|
|
||||||
return slf.newDay
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllPlayerData 获取所有玩家的活动数据
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetAllPlayerData() map[PlayerID]PlayerData {
|
|
||||||
return slf.playerData
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetStartTime 获取活动开始时间
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetStartTime() time.Time {
|
|
||||||
return slf.startTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEndTime 获取活动结束时间
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetEndTime() time.Time {
|
|
||||||
return slf.endTime
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEndOfDistanceTime 获取活动距离结束的时间
|
|
||||||
// - 如果活动已经结束,将返回 <= 0 的数值
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetEndOfDistanceTime() time.Duration {
|
|
||||||
now := slf.offset.Now()
|
|
||||||
return slf.endTime.Sub(now)
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPlayerCount 获取活动玩家数量
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) GetPlayerCount() int {
|
|
||||||
return len(slf.playerData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeletePlayerData 删除特定玩家的活动数据
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) DeletePlayerData(playerId PlayerID) {
|
|
||||||
delete(slf.playerData, playerId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HasPlayer 检查活动中是否存在特定玩家
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) HasPlayer(playerId PlayerID) bool {
|
|
||||||
return hash.Exist(slf.playerData, playerId)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RegStartEvent 活动开始时将立即执行被注册的事件处理函数
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) RegStartEvent(handle StartEventHandle[PlayerID, Data, PlayerData]) {
|
|
||||||
slf.startEventHandles = append(slf.startEventHandles, handle)
|
slf.startEventHandles = append(slf.startEventHandles, handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) onStartEvent() {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) OnStartEvent() {
|
||||||
for _, handle := range slf.startEventHandles {
|
for _, handle := range slf.startEventHandles {
|
||||||
handle(slf)
|
handle(slf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegFinishEvent 活动结束时将立即执行被注册的事件处理函数
|
// RegStopEvent 注册活动结束事件
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) RegFinishEvent(handle FinishEventHandle[PlayerID, Data, PlayerData]) {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) RegStopEvent(handle StopEventHandle[PlayerID, ActivityData, PlayerData]) {
|
||||||
slf.finishEventHandles = append(slf.finishEventHandles, handle)
|
slf.stopEventHandles = append(slf.stopEventHandles, handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) onFinishEvent() {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) OnStopEvent() {
|
||||||
for _, handle := range slf.finishEventHandles {
|
slf.stopTicker()
|
||||||
|
for _, handle := range slf.stopEventHandles {
|
||||||
handle(slf)
|
handle(slf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegNewDayEvent 活动到达新的一天时将立即执行被注册的事件处理函数
|
// RegStartShowEvent 注册活动开始展示事件
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) RegNewDayEvent(handle NewDayEventHandle[PlayerID, Data, PlayerData]) {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) RegStartShowEvent(handle StartShowEventHandle[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
slf.startShowEventHandles = append(slf.startShowEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) OnStartShowEvent() {
|
||||||
|
for _, handle := range slf.startShowEventHandles {
|
||||||
|
handle(slf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegStopShowEvent 注册活动结束展示事件
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) RegStopShowEvent(handle StopShowEventHandle[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
slf.stopShowEventHandles = append(slf.stopShowEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) OnStopShowEvent() {
|
||||||
|
for _, handle := range slf.stopShowEventHandles {
|
||||||
|
handle(slf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegPlayerJoinEvent 注册玩家加入活动事件
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) RegPlayerJoinEvent(handle PlayerJoinEventHandle[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
slf.playerJoinEventHandles = append(slf.playerJoinEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) OnPlayerJoinEvent(playerId PlayerID) {
|
||||||
|
for _, handle := range slf.playerJoinEventHandles {
|
||||||
|
handle(slf, playerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegPlayerLeaveEvent 注册玩家离开活动事件
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) RegPlayerLeaveEvent(handle PlayerLeaveEventHandle[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
slf.playerLeaveEventHandles = append(slf.playerLeaveEventHandles, handle)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) OnPlayerLeaveEvent(playerId PlayerID) {
|
||||||
|
for _, handle := range slf.playerLeaveEventHandles {
|
||||||
|
handle(slf, playerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegNewDayEvent 注册新的一天事件
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) RegNewDayEvent(handle NewDayEventHandle[PlayerID, ActivityData, PlayerData]) {
|
||||||
slf.newDayEventHandles = append(slf.newDayEventHandles, handle)
|
slf.newDayEventHandles = append(slf.newDayEventHandles, handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) onNewDayEvent() {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) OnNewDayEvent() {
|
||||||
for _, handle := range slf.newDayEventHandles {
|
for _, handle := range slf.newDayEventHandles {
|
||||||
handle(slf)
|
handle(slf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegPlayerNewDayEvent 活动玩家到达新的一天时将立即执行被注册的事件处理函数
|
// RegPlayerNewDayEvent 注册玩家新的一天事件
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) RegPlayerNewDayEvent(handle PlayerNewDayEventHandle[PlayerID, Data, PlayerData]) {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) RegPlayerNewDayEvent(handle PlayerNewDayEventHandle[PlayerID, ActivityData, PlayerData]) {
|
||||||
slf.playerNewDayEventHandles = append(slf.playerNewDayEventHandles, handle)
|
slf.playerNewDayEventHandles = append(slf.playerNewDayEventHandles, handle)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) onPlayerNewDayEvent(playerId PlayerID) {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) OnPlayerNewDayEvent(playerId PlayerID) {
|
||||||
for _, handle := range slf.playerNewDayEventHandles {
|
for _, handle := range slf.playerNewDayEventHandles {
|
||||||
handle(slf, playerId)
|
handle(slf, playerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (slf *Activity[PlayerID, Data, PlayerData]) nd(next time.Duration) {
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) refreshTicker(init bool) {
|
||||||
slf.ticker.After(fmt.Sprintf("ACTIVITY_%d_NEW_DAY", slf.id), next, func() {
|
var (
|
||||||
slf.onNewDayEvent()
|
current = activityOffset.Now()
|
||||||
slf.nd(times.Day)
|
start = slf.period.Start()
|
||||||
})
|
end = slf.period.End()
|
||||||
|
)
|
||||||
|
|
||||||
|
if slf.IsOpen() {
|
||||||
|
if init {
|
||||||
|
slf.OnStartEvent()
|
||||||
|
}
|
||||||
|
if distance := end.Sub(current); distance > 0 {
|
||||||
|
ticker.After(fmt.Sprintf(tickerStop, slf.id), distance, slf.OnStopEvent)
|
||||||
|
}
|
||||||
|
ticker.After(fmt.Sprintf(tickerNewDay, slf.id), times.GetNextDayInterval(current), slf.newDay)
|
||||||
|
} else {
|
||||||
|
if distance := start.Sub(current); distance > 0 {
|
||||||
|
ticker.After(fmt.Sprintf(tickerNewDay, slf.id), times.GetNextDayInterval(start), slf.newDay)
|
||||||
|
ticker.After(fmt.Sprintf(tickerStart, slf.id), distance, slf.OnStartEvent)
|
||||||
|
}
|
||||||
|
if end.Before(current) {
|
||||||
|
slf.OnStopEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if slf.IsShow() {
|
||||||
|
if init {
|
||||||
|
slf.OnStartShowEvent()
|
||||||
|
}
|
||||||
|
if distance := slf.afterShow.Sub(current); distance > 0 {
|
||||||
|
ticker.After(fmt.Sprintf(tickerShowStop, slf.id), distance, slf.OnStopShowEvent)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if distance := slf.beforeShow.Sub(current); distance > 0 {
|
||||||
|
ticker.After(fmt.Sprintf(tickerShowStart, slf.id), distance, slf.OnStartShowEvent)
|
||||||
|
}
|
||||||
|
if slf.afterShow.Before(current) {
|
||||||
|
slf.OnStopShowEvent()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) stopTicker() {
|
||||||
|
ticker.StopTimer(fmt.Sprintf(tickerStart, slf.id))
|
||||||
|
ticker.StopTimer(fmt.Sprintf(tickerStop, slf.id))
|
||||||
|
ticker.StopTimer(fmt.Sprintf(tickerShowStart, slf.id))
|
||||||
|
ticker.StopTimer(fmt.Sprintf(tickerShowStop, slf.id))
|
||||||
|
ticker.StopTimer(fmt.Sprintf(tickerNewDay, slf.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) newDay() {
|
||||||
|
current := activityOffset.Now()
|
||||||
|
ticker.After(fmt.Sprintf(tickerNewDay, slf.id), times.GetNextDayInterval(current), slf.newDay)
|
||||||
|
|
||||||
|
last := time.Unix(slf.data.LastNewDay, 0)
|
||||||
|
if !times.IsSameDay(last, times.GetToday(current)) {
|
||||||
|
slf.OnNewDayEvent()
|
||||||
|
}
|
||||||
|
|
||||||
|
for playerId := range slf.data.PlayerData {
|
||||||
|
slf.playerNewDay(playerId, current)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (slf *Activity[PlayerID, ActivityData, PlayerData]) playerNewDay(playerId PlayerID, current time.Time) {
|
||||||
|
last := time.Unix(slf.data.PlayerLastNewDay[playerId], 0)
|
||||||
|
if !times.IsSameDay(last, times.GetToday(current)) {
|
||||||
|
slf.OnPlayerNewDayEvent(playerId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package activity
|
|
||||||
|
|
||||||
type StartEventHandle[PlayerID comparable, Data any, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
|
||||||
type FinishEventHandle[PlayerID comparable, Data any, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
|
||||||
type NewDayEventHandle[PlayerID comparable, Data any, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
|
||||||
type PlayerNewDayEventHandle[PlayerID comparable, Data any, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData], playerId PlayerID)
|
|
|
@ -1,64 +0,0 @@
|
||||||
package activity
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/kercylan98/minotaur/utils/offset"
|
|
||||||
"github.com/kercylan98/minotaur/utils/timer"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Option[PlayerID comparable, Data any, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
|
||||||
|
|
||||||
// WithOffsetTime 通过活动时间使用偏移时间的方式创建活动
|
|
||||||
func WithOffsetTime[PlayerID comparable, Data any, PlayerData any](offsetTime *offset.Time) Option[PlayerID, Data, PlayerData] {
|
|
||||||
return func(activity *Activity[PlayerID, Data, PlayerData]) {
|
|
||||||
activity.offset = offsetTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithGlobalOffsetTime 通过活动时间使用全局偏移时间的方式创建活动
|
|
||||||
func WithGlobalOffsetTime[PlayerID comparable, Data any, PlayerData any]() Option[PlayerID, Data, PlayerData] {
|
|
||||||
return func(activity *Activity[PlayerID, Data, PlayerData]) {
|
|
||||||
activity.offset = offset.GetGlobal()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithTicker 通过使用特定的定时器创建活动
|
|
||||||
func WithTicker[PlayerID comparable, Data any, PlayerData any](ticker *timer.Ticker) Option[PlayerID, Data, PlayerData] {
|
|
||||||
return func(activity *Activity[PlayerID, Data, PlayerData]) {
|
|
||||||
activity.ticker = ticker
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithData 通过使用特定的活动数据创建活动
|
|
||||||
func WithData[PlayerID comparable, Data any, PlayerData any](data Data) Option[PlayerID, Data, PlayerData] {
|
|
||||||
return func(activity *Activity[PlayerID, Data, PlayerData]) {
|
|
||||||
activity.data = data
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithPlayerData 通过使用特定的玩家活动数据创建活动
|
|
||||||
func WithPlayerData[PlayerID comparable, Data any, PlayerData any](playerId PlayerID, playerData PlayerData) Option[PlayerID, Data, PlayerData] {
|
|
||||||
return func(activity *Activity[PlayerID, Data, PlayerData]) {
|
|
||||||
activity.playerData[playerId] = playerData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithPlayerDataFromMap 通过使用 map 中的玩家活动数据创建活动
|
|
||||||
func WithPlayerDataFromMap[PlayerID comparable, Data any, PlayerData any](playerData map[PlayerID]PlayerData) Option[PlayerID, Data, PlayerData] {
|
|
||||||
return func(activity *Activity[PlayerID, Data, PlayerData]) {
|
|
||||||
activity.playerData = playerData
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLastNewDayTime 通过使用特定的玩家最后触发新一天的时间数据创建活动
|
|
||||||
func WithLastNewDayTime[PlayerID comparable, Data any, PlayerData any](playerId PlayerID, newDayTime int64) Option[PlayerID, Data, PlayerData] {
|
|
||||||
return func(activity *Activity[PlayerID, Data, PlayerData]) {
|
|
||||||
activity.newDay[playerId] = newDayTime
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithLastNewDayTimeFromMap 通过使用 map 中的玩家最后触发新一天的时间数据创建活动
|
|
||||||
func WithLastNewDayTimeFromMap[PlayerID comparable, Data any, PlayerData any](newDayTime map[PlayerID]int64) Option[PlayerID, Data, PlayerData] {
|
|
||||||
return func(activity *Activity[PlayerID, Data, PlayerData]) {
|
|
||||||
activity.newDay = newDayTime
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
package activity_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/kercylan98/minotaur/game/activity"
|
||||||
|
"github.com/kercylan98/minotaur/utils/offset"
|
||||||
|
"github.com/kercylan98/minotaur/utils/times"
|
||||||
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestActivity_IsInvalid(t *testing.T) {
|
||||||
|
Convey("TestActivity_IsInvalid", t, func() {
|
||||||
|
offsetTime := offset.NewTime(-time.Now().Sub(time.Date(2023, 06, 28, 13, 0, 0, 0, time.Local)))
|
||||||
|
activity.SetOffsetTime(offsetTime)
|
||||||
|
t.Log(offsetTime.Now())
|
||||||
|
act := activity.NewActivity[int, activity.NoneData, activity.NoneData](1,
|
||||||
|
times.NewPeriod(
|
||||||
|
times.DateWithHMS(2023, 06, 21, 0, 0, 0),
|
||||||
|
times.DateWithHMS(2023, 06, 22, 0, 0, 0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
So(act.IsInvalid(), ShouldBeTrue)
|
||||||
|
|
||||||
|
act = activity.NewActivity[int, activity.NoneData, activity.NoneData](1,
|
||||||
|
times.NewPeriod(
|
||||||
|
times.DateWithHMS(2023, 06, 28, 0, 0, 0),
|
||||||
|
times.DateWithHMS(2023, 06, 29, 0, 0, 0),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
So(act.IsInvalid(), ShouldBeFalse)
|
||||||
|
|
||||||
|
act = activity.NewActivity[int, activity.NoneData, activity.NoneData](1,
|
||||||
|
times.NewPeriod(
|
||||||
|
times.DateWithHMS(2023, 06, 26, 0, 0, 0),
|
||||||
|
times.DateWithHMS(2023, 06, 28, 0, 0, 0),
|
||||||
|
),
|
||||||
|
activity.WithAfterShowTime[int, activity.NoneData, activity.NoneData](times.Day),
|
||||||
|
)
|
||||||
|
So(act.IsInvalid(), ShouldBeFalse)
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package activity
|
||||||
|
|
||||||
|
func newData[PlayerID comparable, ActivityData any, PlayerData any]() *Data[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return &Data[PlayerID, ActivityData, PlayerData]{
|
||||||
|
PlayerData: make(map[PlayerID]PlayerData),
|
||||||
|
PlayerLastNewDay: make(map[PlayerID]int64),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Data 活动数据信息
|
||||||
|
type Data[PlayerID comparable, ActivityData any, PlayerData any] struct {
|
||||||
|
Data ActivityData // 活动全局数据
|
||||||
|
PlayerData map[PlayerID]PlayerData // 活动玩家数据
|
||||||
|
LastNewDay int64 // 最后触发新的一天的时间戳
|
||||||
|
PlayerLastNewDay map[PlayerID]int64 // 玩家最后触发新的一天的时间戳
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package activity
|
||||||
|
|
||||||
|
type (
|
||||||
|
StartEventHandle[PlayerID comparable, Data, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
||||||
|
StopEventHandle[PlayerID comparable, Data, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
||||||
|
StartShowEventHandle[PlayerID comparable, Data, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
||||||
|
StopShowEventHandle[PlayerID comparable, Data, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
||||||
|
PlayerJoinEventHandle[PlayerID comparable, Data, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData], playerId PlayerID)
|
||||||
|
PlayerLeaveEventHandle[PlayerID comparable, Data, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData], playerId PlayerID)
|
||||||
|
NewDayEventHandle[PlayerID comparable, Data, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData])
|
||||||
|
PlayerNewDayEventHandle[PlayerID comparable, Data, PlayerData any] func(activity *Activity[PlayerID, Data, PlayerData], playerId PlayerID)
|
||||||
|
)
|
|
@ -0,0 +1,6 @@
|
||||||
|
package activity
|
||||||
|
|
||||||
|
type activityInterface interface {
|
||||||
|
stopTicker()
|
||||||
|
refreshTicker(init bool)
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
package activity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Option[PlayerID comparable, ActivityData, PlayerData any] func(activity *Activity[PlayerID, ActivityData, PlayerData])
|
||||||
|
|
||||||
|
// WithData 通过指定活动数据的方式来创建活动
|
||||||
|
func WithData[PlayerID comparable, ActivityData, PlayerData any](data *Data[PlayerID, ActivityData, PlayerData]) Option[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return func(activity *Activity[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
activity.data = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithActivityData 通过指定活动全局数据的方式来创建活动
|
||||||
|
// - 该活动数据将会被作为活动的全局数据
|
||||||
|
// - 默认情况下活动本身不包含任何数据
|
||||||
|
func WithActivityData[PlayerID comparable, ActivityData, PlayerData any](data ActivityData) Option[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return func(activity *Activity[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
activity.data.Data = data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPlayerDataLoadHandle 通过指定玩家数据加载函数的方式来创建活动
|
||||||
|
// - 该函数将会在玩家数据加载时被调用
|
||||||
|
// - 活动中的玩家数据将会被按需加载,只有在玩家加入活动时才会被加载
|
||||||
|
func WithPlayerDataLoadHandle[PlayerID comparable, ActivityData, PlayerData any](handle func(activity *Activity[PlayerID, ActivityData, PlayerData], playerId PlayerID) PlayerData) Option[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return func(activity *Activity[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
activity.playerDataLoadHandle = handle
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithBeforeShowTime 通过指定活动开始前的展示时间的方式来创建活动
|
||||||
|
func WithBeforeShowTime[PlayerID comparable, ActivityData, PlayerData any](showTime time.Duration) Option[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return func(activity *Activity[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
activity.beforeShow = activity.period.Start().Add(-showTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithAfterShowTime 通过指定活动结束后的展示时间的方式来创建活动
|
||||||
|
func WithAfterShowTime[PlayerID comparable, ActivityData, PlayerData any](showTime time.Duration) Option[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return func(activity *Activity[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
activity.afterShow = activity.period.End().Add(showTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithLastNewDay 通过指定活动最后触发新的一天的时间戳的方式来创建活动
|
||||||
|
func WithLastNewDay[PlayerID comparable, ActivityData, PlayerData any](lastNewDay int64) Option[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return func(activity *Activity[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
activity.data.LastNewDay = lastNewDay
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithPlayerLastNewDay 通过指定玩家最后触发新的一天的时间戳的方式来创建活动
|
||||||
|
func WithPlayerLastNewDay[PlayerID comparable, ActivityData, PlayerData any](playerLastNewDay map[PlayerID]int64) Option[PlayerID, ActivityData, PlayerData] {
|
||||||
|
return func(activity *Activity[PlayerID, ActivityData, PlayerData]) {
|
||||||
|
activity.data.PlayerLastNewDay = playerLastNewDay
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue