Documentation
¶
Overview ¶
Package effects models transient compositional lifecycle state: an entity carries a set of timed effects, each with apply/tick/expire/ refresh hooks, that can stack with rules (refresh, add, ignore, replace). Named generically because the pattern is domain-free.
Common applications:
- Games — buffs/debuffs/DoTs/auras carried by a player or NPC.
- Trading-bot — signal modifiers ("buy weight decays over 30min"), volatility scalings, regime overlays.
- Observability — alert states with debounce / suppression / escalation.
- Rate-limit windows with TTL stacking.
Trackers are single-writer: one Tracker per subject. For multi-subject systems hold a Tracker map keyed by subject ID and own each from a dedicated goroutine (typically via kit/actor).
Index ¶
- type ActiveEffect
- type Effect
- type EffectID
- type StackPolicy
- type Tracker
- func (t *Tracker[D]) Apply(ctx context.Context, e Effect[D], data *D, now time.Time) bool
- func (t *Tracker[D]) Has(id EffectID) bool
- func (t *Tracker[D]) Len() int
- func (t *Tracker[D]) Remove(ctx context.Context, id EffectID, data *D)
- func (t *Tracker[D]) Snapshot() []ActiveEffect
- func (t *Tracker[D]) Tick(ctx context.Context, data *D, now time.Time, dt time.Duration)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ActiveEffect ¶
type ActiveEffect struct {
ID EffectID
Stacks int
AppliedAt time.Time
ExpiresAt time.Time // zero for infinite-duration effects
}
ActiveEffect is the public snapshot of an effect instance held by a Tracker.
type Effect ¶
type Effect[D any] interface { // ID returns the unique identifier for this effect kind. Two effects // with the same ID are considered the same kind for stacking purposes. ID() EffectID // Stack returns the policy applied when this effect is added to a // subject that already carries an effect with the same ID. Stack() StackPolicy // MaxStacks is the cap for StackAdd; ignored for other policies. // Zero means unlimited. MaxStacks() int // Duration is how long this effect lasts from Apply. Zero means // the effect persists until Remove is called explicitly. Duration() time.Duration // OnApply runs when the effect is first added (or when StackReplace // replaces a prior instance). OnApply(ctx context.Context, data *D, stacks int) // OnTick runs once per Tracker.Tick call while the effect is active. // dt is the elapsed time since the previous Tick. OnTick(ctx context.Context, data *D, stacks int, dt time.Duration) // OnRefresh runs when the effect's duration is reset (StackRefresh // or StackAdd applied to existing). OnRefresh(ctx context.Context, data *D, stacks int) // OnExpire runs when the effect's duration elapses or it is removed. OnExpire(ctx context.Context, data *D, stacks int) }
Effect is the unit of transient state. Implementations are typically small value types or stateless singletons that read/write a shared per-subject data block D via the lifecycle hooks.
type EffectID ¶
type EffectID uint32
EffectID identifies an effect kind. Two effects with the same ID applied to the same Tracker interact via StackPolicy.
type StackPolicy ¶
type StackPolicy uint8
StackPolicy controls what happens when an effect is applied to a Tracker that already holds one with the same EffectID.
const ( // StackRefresh resets the existing effect's duration; stack count // is unchanged. Default behaviour for most game buffs. StackRefresh StackPolicy = iota // StackAdd increments the stack count (up to MaxStacks) and refreshes // duration. Use for damage-over-time effects that stack intensity. StackAdd // StackIgnore rejects the new application if one already exists. StackIgnore // StackReplace replaces the existing effect with the new one. StackReplace )
type Tracker ¶
type Tracker[D any] struct { // contains filtered or unexported fields }
Tracker holds the active effect set for a single subject. Single- writer by design: Apply/Tick/Remove must be called from one goroutine (typically the actor owning the subject).
func (*Tracker[D]) Apply ¶
Apply adds or stacks an effect on the subject, returning true if the application took effect (false for StackIgnore against an existing instance). The data pointer is passed through to the effect's hooks so it can mutate per-subject state (HP, signal weight, etc.).
func (*Tracker[D]) Remove ¶
Remove removes the effect with the given ID, firing OnExpire. No-op if the effect is not present.
func (*Tracker[D]) Snapshot ¶
func (t *Tracker[D]) Snapshot() []ActiveEffect
Snapshot returns a copy of the active effect list for replication or inspection. The returned slice is owned by the caller.