Thread
Threads utilize the coroutines of the underlying script VM and are designed to execute loops or delays without blocking the game. A thread exists until its code has finished executing or it is manually terminated using Kill.
All threads run sequentially (synchronously) on the main thread. Therefore, there is no need to worry about writing thread-safe code.
If you use an infinite loop inside a thread, you must call Thread.Pause() (even Thread.Pause(0) is sufficient). Failing to do so will freeze the entire game or server, as the thread will never yield control back to the main thread.
Quick Reference
| Name | Type | Description |
|---|---|---|
| Create | Shared | Creates a new thread with a function that is executed asynchronously. |
| Pause | Shared | Pauses the current thread for the specified duration. |
| Kill | Shared | Kills the specified thread on the next script tick. |
| IsAlive | Shared | Returns whether the specified thread is currently alive and running. |
Timing
Threads are driven by the script tick. A thread never runs between two ticks, and everything below follows from that:
- The server ticks about every 10 ms, plus however long the tick itself takes.
- The client ticks once per rendered frame: roughly 16 ms at 60 FPS, 33 ms at 30 FPS, and much longer while the game stutters or streams.
Thread.Pause(ms) therefore means at least ms, never exactly ms. The thread wakes up on the
first tick after the time has elapsed, so a pause is always rounded up to the next tick.
Do not use Thread.Pause to measure time. Adding up the values you passed to it does not give
you elapsed milliseconds, because the error accumulates with every iteration and is different on every
machine. Thread.Pause(1) in a loop is not a millisecond counter: on the server it advances in
steps of roughly 10 ms, on the client in frames.
To measure time, read a clock instead:
- Lua
- Squirrel
Thread.Create(function()
-- Client: milliseconds since the game started.
local start = Game.GetGameTimer()
while Game.GetGameTimer() - start < 5000 do
Thread.Pause(0)
end
Chat.AddMessage("Five seconds are really over.")
end)
Thread.Create(function() {
// Client: milliseconds since the game started.
local start = Game.GetGameTimer();
while (Game.GetGameTimer() - start < 5000) {
Thread.Pause(0);
}
Chat.AddMessage("Five seconds are really over.");
});
On the server there is no game timer; Lua's os.time() gives you whole seconds.
Thread.Pause(0) means "give up the rest of this tick and continue on the next one". It is the
right call inside a loop that has to run every frame. A smaller value buys you nothing, because
the next tick is the earliest the thread can continue either way.
Shared Functions
Create
Creates a thread with a function that is executed on the next script tick.
int threadId = Thread.Create(function threadFunc, any ...)
The function does not start running inside Thread.Create, not even its first line. It runs
for the first time on the next tick, so anything the calling code does afterwards still happens
first.
Example:
- Lua
- Squirrel
Events.Subscribe("sessionInit", function()
Thread.Create(function()
-- Get player model hash key.
local playerModel = Game.GetHashKey("M_Y_MULTIPLAYER")
-- Request model.
Game.RequestModel(playerModel)
-- Wait until model loaded.
while not Game.HasModelLoaded(playerModel) do
Game.RequestModel(playerModel)
Thread.Pause(0)
end
-- Change player model.
Game.ChangePlayerModel(Game.GetPlayerId(), playerModel)
-- Release model.
Game.MarkModelAsNoLongerNeeded(playerModel)
end)
end)
Events.Subscribe("sessionInit", function() {
Thread.Create(function() {
// Get player model hash key.
local playerModel = Game.GetHashKey("M_Y_MULTIPLAYER");
// Request model.
Game.RequestModel(playerModel);
// Wait until model loaded.
while (!Game.HasModelLoaded(playerModel)) {
Game.RequestModel(playerModel);
Thread.Pause(0);
}
// Change player model.
Game.ChangePlayerModel(Game.GetPlayerId(), playerModel);
// Release model.
Game.MarkModelAsNoLongerNeeded(playerModel);
});
});
Pause
Pauses the current thread for at least the specified duration. See Timing.
Thread.Pause(int milliseconds)
This function can only be used within a running thread. Calling it anywhere else raises a script error.
The comment in the example below is a simplification: the message is sent no sooner than every 5 seconds, and the small overshoot of each round adds up over time. If a task has to happen at an exact wall-clock moment, compare against a clock instead of relying on the pause.
Example:
- Lua
- Squirrel
Events.Subscribe("scriptInit", function()
Thread.Create(function()
while true do
Thread.Pause(5000)
-- This message is sent every 5 seconds.
Chat.AddMessage("5 seconds are over.")
end
end)
end)
Events.Subscribe("scriptInit", function() {
Thread.Create(function() {
while (true) {
Thread.Pause(5000);
// This message is sent every 5 seconds.
Chat.AddMessage("5 seconds are over.");
}
});
});
Kill
Kills the specified thread on the next script tick.
Thread.Kill(int threadId)