SwiftlyS2
Development

Scheduler

SwiftlyS2 provides a scheduler service for main-thread dispatching and timer-based execution.

Accessing Scheduler Service

Scheduler APIs are available through Core.Scheduler.

public override void Load(bool hotReload)
{
  var scheduler = Core.Scheduler;
}

Scheduling on the Main Loop

Next Tick

Use NextTick(Action) to run a callback on the next server tick.

Core.Scheduler.NextTick(() =>
{
  Console.WriteLine("Runs on next tick.");
});

Next World Update

Use NextWorldUpdate(Action) to run a callback on the next world update phase.

Core.Scheduler.NextWorldUpdate(() =>
{
  Console.WriteLine("Runs on next world update.");
});

Awaitable Variants

You can await scheduler execution using the non-async callback overloads:

await Core.Scheduler.NextTickAsync(() =>
{
  Console.WriteLine("Executed in next tick, awaited.");
});

int computedValue = await Core.Scheduler.NextWorldUpdateAsync(() =>
{
  return 42;
});

Do not pass async callbacks (Func<Task...>) to NextTick or NextWorldUpdate families. Those overloads are obsolete and documented as unsafe.

Timer APIs

Tick-Based Timers

Tick-based APIs use game ticks as units.

var delayCts = Core.Scheduler.Delay(128, () =>
{
  Console.WriteLine("Executed once after 128 ticks.");
});

var repeatCts = Core.Scheduler.Repeat(64, () =>
{
  Console.WriteLine("Runs immediately, then every 64 ticks.");
});

var delayRepeatCts = Core.Scheduler.DelayAndRepeat(32, 64, () =>
{
  Console.WriteLine("Starts after 32 ticks, then repeats every 64 ticks.");
});

Second-Based Timers

Use second-based APIs for easier human-readable intervals.

Core.Scheduler.DelayBySeconds(2.0f, () =>
{
  Console.WriteLine("Executed after 2 seconds.");
});

Core.Scheduler.RepeatBySeconds(1.0f, () =>
{
  Console.WriteLine("Runs immediately, then every 1 second.");
});

Core.Scheduler.DelayAndRepeatBySeconds(3.0f, 1.5f, () =>
{
  Console.WriteLine("Starts after 3 seconds, then repeats every 1.5 seconds.");
});

Second-based timers are still driven by game ticks, so very small intervals approach tick precision limits (about 15 ms).

Canceling Timers

All timer creation methods return CancellationTokenSource.

var token = Core.Scheduler.Repeat(64, () => Console.WriteLine("Tick"));

// Manual cancel
token.Cancel();

// Auto-cancel on map change
Core.Scheduler.StopOnMapChange(token);

Advanced Timers with AddTimer

Use AddTimer(Func<ITimerContext, TimerStep>) when you need dynamic behavior per execution.

var cts = Core.Scheduler.AddTimer(ctx =>
{
  Console.WriteLine($"Run #{ctx.ExecutionCount}");

  if (ctx.ExecutionCount >= 4)
  {
    return TimerStep.Stop();
  }

  return TimerStep.WaitForSeconds(1.0f);
});

ITimerContext.ExecutionCount starts at 0 for the first run.

Common TimerStep helpers:

  • TimerStep.Spin() to run again on next tick
  • TimerStep.WaitForTicks(long ticks)
  • TimerStep.WaitForMilliseconds(long milliseconds)
  • TimerStep.WaitForSeconds(float seconds)
  • TimerStep.Stop()

Reference

See ISchedulerService for scheduler methods.

See ITimerContext and TimerStep for advanced timer flow.

On this page