Live Event Scheduler
Module — 2 files
These files compile together (same module folder).
cue_based_event.verse
using { /Verse.org/SceneGraph }
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
# Strategy interface for event scheduling
schedule_strategy<public> := interface<castable>(p_interface):
ScheduleEvent<public>(Event:live_event)<suspends>:void
<#
Sleep strategy: Sleeps until event time
Note: Sleep() uses simulation time, so server lag can cause delays
#>
sleep_schedule_strategy<public> := class<concrete>(schedule_strategy):
ScheduleEvent<override>(Event:live_event)<suspends>:void=
# If recurring event timestamp is in the past, jump to next future occurrence
if (RecurInterval := Event.RecurEvery?):
CurrentTime := GetSecondsSinceEpoch()
if (Event.Timestamp < CurrentTime):
TimeSinceStart := CurrentTime - Event.Timestamp
PeriodsToAdd := Ceil[TimeSinceStart / RecurInterval] or 1
set Event.Timestamp = Event.Timestamp + (PeriodsToAdd * RecurInterval)
loop:
CurrentTime := GetSecondsSinceEpoch()
TimeUntilEvent := Event.Timestamp - CurrentTime
if (TimeUntilEvent <= 0.0):
spawn. Event.RunEvent()
# Schedule next occurrence
if (RecurInterval := Event.RecurEvery?):
TimeSinceTrigger := CurrentTime - Event.Timestamp
PeriodsToAdd := Max(1, Ceil[TimeSinceTrigger / RecurInterval]) or 1
set Event.Timestamp = Event.Timestamp + (PeriodsToAdd * RecurInterval)
else:
break
else:
Sleep(TimeUntilEvent)
<#
Polling strategy: Checks every PollInterval seconds
Note: Not affected by simulation lag, but has continuous polling overhead
#>
polling_schedule_strategy<public> := class<concrete>(schedule_strategy):
@editable
PollInterval : float = 1.0
ScheduleEvent<override>(Event:live_event)<suspends>:void=
# Jump to next future occurrence if timestamp is in the past
if (RecurInterval := Event.RecurEvery?):
CurrentTime := GetSecondsSinceEpoch()
if (Event.Timestamp < CurrentTime):
TimeSinceStart := CurrentTime - Event.Timestamp
PeriodsToAdd := Ceil[TimeSinceStart / RecurInterval] or 1
set Event.Timestamp = Event.Timestamp + (PeriodsToAdd * RecurInterval)
loop:
CurrentTime := GetSecondsSinceEpoch()
if (CurrentTime >= Event.Timestamp):
spawn. Event.RunEvent()
# Skip to next future occurrence
if (RecurInterval := Event.RecurEvery?):
TimeSinceTrigger := CurrentTime - Event.Timestamp
PeriodsToAdd := Max(1, Ceil[TimeSinceTrigger / RecurInterval]) or 1
set Event.Timestamp = Event.Timestamp + (PeriodsToAdd * RecurInterval)
else:
break
Sleep(PollInterval)
# Interface for schedulers. Provides default implementation
schedulable<public> := interface<castable>(p_interface):
@editable
EventsToSchedule<public> : []entity = array{}
@editable
Strategy<public> : schedule_strategy = sleep_schedule_strategy{}
# Default implementation: gets live_event components from entities and schedules them
ScheduleAllEvents<public>():void=
for:
EventEntity : EventsToSchedule
LiveEventComponent : EventEntity.GetComponentsWithInterface(live_event)
do:
spawn. Strategy.ScheduleEvent(LiveEventComponent)
# Main scheduler component
live_event_scheduler_component<public> := class<final_super>(component, schedulable):
OnBeginSimulation<override>():void=
(super:)OnBeginSimulation()
ScheduleAllEvents()
live_event<public> := interface<unique><castable>(p_interface):
@editable
var Timestamp<public> : float = 0.0 # Unix epoch seconds when event should trigger
@editable
RecurEvery<public> : ?float = false # Optional: recur every N seconds
RunEvent<public>()<suspends>:void # Event implementation
# Cue-based event: Non-programmers can create events in the editor
cue_based_event := class<concrete>(creative_device, live_event):
@editable
Cue : cue_sequence = cue_sequence{}
RunEvent<override>()<suspends>:void=
Cue.Invoke()
return
cue := class<abstract>:
Invoke()<suspends>:void
cue_group := class<abstract>(cue):
@editable
Cues : []cue = array{}
Invoke<override>()<suspends>:void
cue_sequence := class<concrete>(cue_group):
Invoke<override>()<suspends>:void=
for (Cue : Cues):
Cue.Invoke()
cue_parallell := class<concrete>(cue_group):
Invoke<override>()<suspends>:void=
RecursiveSync(for (Cue : Cues) { Cue.Invoke })
cue_print := class<concrete>(cue):
@editable
Text : string = ""
Invoke<override>():void=
Print(Text)
cue_wait := class<concrete>(cue):
@editable
Time : float = 1.0
Invoke<override>()<suspends>:void=
Sleep(Time)
cue_play_sequencer := class<concrete>(cue):
@editable
Sequencer : cinematic_sequence_device = cinematic_sequence_device{}
@editable
WaitForFinish : logic = false
Invoke<override>()<suspends>:void=
Sequencer.Play()
if (WaitForFinish = true):
Sequencer.StoppedEvent.Await()
cue_trigger := class<concrete>(cue):
@editable
TriggerDevice : trigger_device = trigger_device{}
Invoke<override>()<suspends>:void=
TriggerDevice.Trigger()
# Example event
example_event := class<final_super>(component, live_event):
RunEvent<override>()<suspends>:void=
Print("Event just started!")
Sleep(5.0)
Print("Event is finished!")
Sign in to download module
Copy-paste each file above is always free.