TaskLib provides a coroutine scheduler for Payday 2. It hooks into the CoreSetup update loop, allowing developers to write asynchronous logic providing easier loop, waits and events managment.
TaskLib is accessible globally via the task table (or _G.task). Because it automatically hooks into CoreSetup:__update, tasks are processed automatically each frame.
❗Important: Any function that yields (e.g., task.wait() or task.WaitForChild()) must be executed within a coroutine initialized by this library.
Note: For stability reasons, there is a hardcoded limit of 100 task resumes per frame. If this limit is reached, any remaining tasks will be skipped for the current frame and resumed during the next one. (You will probably never reach that limit though)
These functions are used to initialize, terminate, and monitor tasks.
Creates and immediately schedules a new coroutine.
(Alias: task.new)
- func (function): The function to execute asynchronously.
- name (string, optional): An identifier for the task, useful for debugging. Defaults to "Task#ID".
- run_once (boolean, optional): If true, the task is removed from the scheduler after its initial execution, regardless of yielding. Defaults to false.
- Returns: TaskHandle table.
Example:
local myTask = task.spawn(function()
log("Task initialized.")
task.wait(2)
log("Task completed after 2 seconds.")
end, "InitializationTask")Terminates a scheduled task. The task is marked as cancelled and will be removed during the next frame update.
- handle (TaskHandle): The handle returned by task.spawn().
Example:
local myTask = task.spawn(function()
task.wait(10)
log("This will not print.")
end)
-- Cancel the task before it can finish waiting
task.cancel(myTask)Evaluates whether a specific task is currently active in the scheduler.
- handle (TaskHandle): The task handle to check.
- Returns: true if the task is active and has not been cancelled or finished; false otherwise.
Example:
local myTask = task.spawn(function()
task.wait(5)
end)
log(tostring(task.isRunning(myTask))) -- Outputs: trueLogs and returns the total number of currently active tasks. Useful for memory management and debugging runaway loops.
- Returns: (number) The count of active tasks.
Example:
local activeCount = task.stats()
if activeCount > 50 then
log("[Warning] High number of active tasks: " .. tostring(activeCount))
endThese functions yield the current coroutine. They must be called from within a task.spawn callback.
Yields the current coroutine for a specified duration.
- seconds (number, optional): The duration to yield in seconds. If omitted or set to 0, it yields for exactly one frame.
Example:
task.spawn(function()
log("Starting sequence...")
task.wait(2.5) -- Yields for 2.5 seconds
log("Sequence complete.")
end)Yields the coroutine until a specific key is populated within a parent table.
- parent (table): The table to monitor.
- childName (string/any): The key expected to be assigned in the parent table.
- timeoutSeconds (number, optional): Maximum time to wait in seconds. If exceeded, logs a warning and returns nil.
- Note: Infinite yield possible if timeout isn't provided and child never appears!
- Returns: The assigned value (child), or nil if the timeout is reached.
Example:
task.spawn(function()
-- Wait up to 10 seconds for the player manager to initialize
local loc = task.WaitForChild(managers, "player", 10)
if loc then
log("Player manager is ready.")
else
log("[Error] Player manager failed to load within timeout.")
end
end)Utility wrappers for common delay and interval operations. These do not require manual coroutine initialization.
Executes a callback function after a specified delay.
(Alias: task.timeout)
- seconds (number): Delay in seconds.
- callback (function): The function to execute.
- name (string, optional): Task identifier.
Example:
task.delay(3, function()
log("This executes exactly 3 seconds later.")
end, "DelayedLog")Executes a callback repeatedly at a specified interval. The loop terminates if the callback explicitly returns false.
(Alias: task.loopInf)
- intervalSeconds (number): Time between executions in seconds.
- callback (function): The function to execute.
- name (string, optional): Task identifier.
Example:
task.every(1, function()
local player = managers.player:player_unit()
if not alive(player) then
return false -- Returning false cancels the interval loop
end
local damage_ext = player:character_damage()
if damage_ext:get_real_health() < damage_ext:_max_health() then
damage_ext:restore_health(5, false)
return true
end
return false -- Cancel loop once fully healed
end, "RegenLoop")Executes a callback repeatedly at a specified interval, terminating automatically when the maximum duration is reached.
- intervalSeconds (number): Time between executions.
- callback (function): The function to execute. Can return false to terminate early.
- timeoutSeconds (number): The maximum total runtime for the loop.
- name (string, optional): Task identifier.
Example:
task.loopUntil(0.5, function()
log("Checking conditions...")
-- Code here
return true
end, 5.0, "StartupCheck")
-- This will log every 0.5 seconds for a maximum of 5 seconds.You have several ways to stop a loop or timeout task.
- Recommended Way
- Every task receives itself as the first argument. You can call :cancel() directly on it to stop execution.
local count = 0
task.loopInf(0.5, function(loop)
count = count + 1
log("Looping...")
if count >= 15 then
loop:cancel() -- Clean and easy!
end
end)- Self-Termination Way
- If your callback function returns false, the scheduler will automatically kill the task.
task.every(1, function()
if some_condition_met then
return false
end
end)- The External Way
- If you have the task handle stored in a variable, you can cancel it from outside the task.
local my_timer = task.timeout(10, function() ... end)
-- Cancel via API call:
task.cancel(my_timer)
-- Or cancel via property:
my_timer.cancelled = true