Skip to main content

Timers

Namespace: DeadworksManaged.Api

The timer system provides per-plugin scheduling for delayed and repeating actions. Access via this.Timer in any DeadworksPluginBase subclass.

Duration

Strongly-typed time value representing either game ticks or wall-clock time. Created via extension methods:

// Tick-based (tied to server tick rate)
1.Ticks() // 1 game tick
64.Ticks() // 64 game ticks

// Real-time
3.Seconds() // 3 seconds
1.5.Seconds() // 1.5 seconds
500.Milliseconds() // 500ms
1700.Milliseconds() // 1.7 seconds

Extension Methods

MethodInput TypeDescription
.Ticks()int, longCreate tick-based duration
.Seconds()int, doubleCreate real-time duration
.Milliseconds()int, longCreate duration in milliseconds

ITimer

Per-plugin timer service. Accessed via this.Timer property.

Timer.Once

Execute a callback once after a delay:

Timer.Once(5.Seconds(), () =>
{
Console.WriteLine("5 seconds have passed!");
});

Timer.Every

Execute a callback repeatedly at a fixed interval. Returns an IHandle for cancellation:

var handle = Timer.Every(1.Ticks(), () =>
{
// Runs every tick
pawn.AbilityComponent.ResourceStamina.CurrentValue =
pawn.AbilityComponent.ResourceStamina.MaxValue;
});

// Cancel later
handle.Cancel();

Timer.Sequence

Run a stateful sequence with control over pacing. Useful for multi-step timed effects:

Timer.Sequence(step =>
{
// step.Run = how many times invoked (starts at 1)
// step.ElapsedTicks = ticks since sequence started

if (step.Run > 10)
return step.Done(); // End the sequence

// Apply damage each step
target.Hurt(damagePerTick, attacker, attacker, null);

return step.Wait(200.Milliseconds()); // Wait before next step
});

Timer.NextTick

Defer an action to the next game tick. Thread-safe:

Timer.NextTick(() =>
{
// Runs on the next tick
});

IHandle

Handle to a scheduled timer for cancellation and status checking.

MemberTypeDescription
Cancel()voidCancel this timer. No-op if already finished
IsFinishedboolWhether this timer has completed or been cancelled

IStep

Sequence step context passed to Timer.Sequence callbacks.

MemberTypeDescription
RunintHow many times the callback has been invoked (starts at 1)
ElapsedTickslongGame ticks elapsed since the sequence started
Wait(Duration)PaceExecute again after specified duration
Done()PaceEnd the sequence

Patterns

Per-Entity Timer Tracking

Use EntityData<IHandle?> to track timers per entity:

private readonly EntityData<IHandle?> _timers = new();

// Start a timer for a specific entity
var timer = Timer.Every(1.Ticks(), () => { /* ... */ });
_timers[pawn] = timer;

// Cancel later
if (_timers.TryGet(pawn, out var t))
{
t?.Cancel();
_timers.Remove(pawn);
}

Toggle Pattern

private readonly EntityData<IHandle?> _activeTimers = new();

[ChatCommand("stamina")]
public HookResult OnStamina(ChatCommandContext ctx)
{
var pawn = ctx.Controller?.GetHeroPawn();
if (pawn == null) return HookResult.Handled;

// If already active, cancel (toggle off)
if (_activeTimers.TryGet(pawn, out var existing) && existing != null)
{
existing.Cancel();
_activeTimers.Remove(pawn);
return HookResult.Handled;
}

// Start new timer (toggle on)
var timer = Timer.Every(1.Ticks(), () =>
{
if (pawn.Health <= 0) return;
var stamina = pawn.AbilityComponent.ResourceStamina;
stamina.LatchValue = stamina.MaxValue;
stamina.CurrentValue = stamina.MaxValue;
});
_activeTimers[pawn] = timer;

return HookResult.Handled;
}

Cleanup

Timers are per-plugin and automatically cleaned up when the plugin is unloaded. For manual cleanup in OnUnload():

public override void OnUnload()
{
// EntityData stores will be cleaned up,
// but you can manually cancel if needed
}

See Also