Threads, Signals and Flags¶
Threads¶
Squirrel allows scripts to spin off function calls in a thread. All subsequential function calls will be threaded as well.
In threaded functions, it's possible to halt a threaded function with wait
statements, signals, flags and by suspending a thread object.
You can use the IsNewThread()
function to determine if the current function is threaded off.
For more information, check out the squirrel documentation on threads and sq functions of threads. squirrel_re is very similar to vanilla squirrel in this regard.
A thread is considered finished, after the threaded function returned a value. This may be null
.
Spinning off a thread¶
To create a new coroutine, call a function with the thread
keyword before.
thread void function(){}()
thread MyFunction()
To get a thread object, use the newthread
function.
void function CoroutineExample()
{
suspend( "passback" ) // passback is optional
print( "threaded statement" )
}
var co = newthread( CoroutineExample )
var suspendedReturn = co.call() // you NEED to use .call, invoking the function with () won't work
co.wakeup() // continue thread
wait¶
The wait
statement halts threads for a set amount of time specified after the wait
keyword. Integers and floats are accepted as times in seconds.
void function WaitExample( float n )
{
wait 1 // wait 1 second
wait n // wait n seconds
}
thread WaitExample( 0.5 ) // thread will halt for a total 1.5 seconds
To wait a single frame, don't use wait 0
since it doesn't actually wait a game frame. For example, if you have a client loop that does wait 0 even if the game is paused the loop will still run. Use WaitFrame()
instead.
When using infinite loops it's important to work with wait
statements to avoid the game freezing.
If you want to wait until a thread is finished, you can spin off the thread that you wait for with the waitthread
keyword.
void function ParentThread()
{
printt( "pre spinoff " + string( Time() ) )
waitthread void function()
{
printt( "mid spinoff " + string( Time() ) )
wait 1
}
printt( "post spinoff" + string( Time() ) )
}
OnThreadEnd¶
Use the OnThreadEnd
function to execute a callback after a thread has ended. This is useful for cleanup functions that remove entities after they're used or similar.
void function PlayIncomingFX( vector origin, int teamNum )
{
wait 1.50
EmitSoundAtPosition( teamNum, origin, "Titan_1P_Warpfall_Start" )
local colorVec = Vector( 0, 255, 0 )
entity cpoint = CreateEntity( "info_placement_helper" )
SetTargetName( cpoint, UniqueString( "pickup_controlpoint" ) )
DispatchSpawn( cpoint )
cpoint.SetOrigin( colorVec )
entity glowFX = PlayFXWithControlPoint( INCOMING_SPAWN_FX, origin, cpoint, -1, null, null, C_PLAYFX_LOOP )
OnThreadEnd(
function() : ( glowFX, cpoint )
{
if ( IsValid( glowFX ) )
glowFX.Destroy()
if ( IsValid( cpoint ) )
cpoint.Destroy()
}
)
wait 1.25
}
Example Script¶
void function SetPositionDelayed( entity ent, vector pos, float delay )
{
wait delay
ent.SetOrigin( pos )
}
SetPositionDelayed( player, <0, 0, 100>, 5.0 )
SetPositionDelayed( player, <0, 0, 50>, 2.5 ) // this will finish sooner.
Signals and flags¶
Signals and flags allow threads to wait for events before running some code.
Signals¶
void RegisterSignal( string signal )
Registers a Signals to use on any entity. It's required to register signals before using them.
CBaseEntity
void Signal( string signal, table results = null )
Trigger a signal on this entity. The passed results
will be returned by WaitSignal
.
void EndSignal( string signal )
Ends this thread when the identifier is signaled on this entity
table WaitSignal( string signal )
Halts this thread until a signal is activated for this entity
void ConnectOutput( string signal, void functionref( entity trigger, entity activator, entity caller, var value ) callback )
Register a callback that executes when the signal
has been fired on this Entity
void DisconnectOutput( string event, void functionref( entity trigger, entity activator, entity caller, var value ) callback )
Disconnects the callback from the signal.
void AddOutput( string outputName, string | entity target, string inputName, string parameter = "", float delay = 0, float maxFires = 0 )
Connects an output on this entity to an input on another entity via code. The target
can be a name or a named entity.
void Fire( string signal, string param = "", float delay = 0, entity activator = null, entity caller = null )
Fire a signal on this entity, with optional parm and delay
void FireNow( string output, string param = "", float delay = 0, entity activator = null, entity caller = null )
Fire a signal on this entity, with optional parm and delay (synchronous)
It's also possible to trigger and catch signals with methods that aren't properties of an entity.
void Signal( var obj, string signal, table results = null )
Trigger a signal on ent
. The passed results
will be returned by WaitSignal
.
table WaitSignal( entity ent, ... )
Wait for any of the passed signals to be triggered.
// Wait for the NPC to die, delete, or get leeched, then remove the npc from the array
WaitSignal( ent, "OnDeath", "OnDestroy", "OnLeeched" )
void EndSignal( var obj, string signal )
Ends this thread when the identifier is signaled on ent
Example¶
For example, if we want to tell a player not to give up after being killed several times, we can write it this way:
// First, we register signal we want to use
RegisterSignal("OnMultipleDeaths")
void function WatchForDeaths (entity player)
{
int deathsCount = 0
while( GamePlayingOrSuddenDeath() )
{
if ( player.isDead() ) // This doesn't exist, don't try this at home
{
deathsCount += 1
if (deathsCount >= 42)
{
// This sends "OnMultipleDeaths" signal on player entity
player.Signal( "OnMultipleDeaths" )
}
}
}
}
void function DontGiveUp (entity player)
{
// This is a blocking call
player.WaitSignal("OnMultipleDeaths");
// This will not run until entity received "OnMultipleDeaths" signal
SendHudMessage( player, "Don't give up!", -1, 0.4, 255, 0, 0, 0, 0, 3, 0.15 )
}
// Launch our methods in dedicated threads
entity player = GetPlayerArray()[0]
thread WatchForDeaths( player )
thread DontGiveUp( player )
In this example, the DontGiveUp
method is launched at the same time as WatchForDeaths
; but it will not
run until player died 42 times.
When you want your thread to die on a given event, you can use entity.EndSignal( "OnMultipleDeaths" )
; when said signal
is set, thread will end (after calling any OnThreadEnd
methods).
Flags¶
Flags
work pretty much the same way as Signals
, except they can be set up without target entity:
void FlagInit( string flag, bool isSet = false )
Create a flag
void FlagWait( string flag )
Halts a thread until a flag is set. Callee must be threaded off.
void FlagWaitAll( ... )
Halts until every passed flag is set. Callee must be threaded off.
void FlagWaitWithTimeout( string flag, float timeOut )
Halts until the passed flag is set or the timer runs out. Callee must be threaded off.
void FlagSet( string flag )
Raise a flag
void FlagSetOnFlag( string flagset, string flagwait, float delay = 0 )
Set flagset
after flagwait
is set and the delay is met.
void FlagClear( string flag )
Reset a flag
void FlagWaitClearAll( ... )
Resets all passed flags.
void FlagClearOnFlag( string flagclear, string flagwait )
Reset flagclear
when flagwait
is set.
void FlagWaitClearWithTimeout( string flag, float timeOut )
Resets a flag after the timer runs out.
void FlagWaitClearAny( ... )
Wait until any passed flag is cleared.
void FlagClearEnd( string flag )
void FlagToggle( string flag )
Raise a flag if it is reset, or reset it if it's raised.
void FlagEnd( string flag )
Ends this thread when the flag is set
bool Flag( string flag )
Returns the current state of a flag.
bool FlagExists( string flag )
Returns true
if the flag is initialized
array
Splits the flag on " "
array
Splits the value of the keyvalues of the entity on the index field
on " "
Example¶
void function FlagExample()
{
FlagInit( "BombHasExploded" )
thread BombTicker()
FlagWait( "BombHasExploded" )
print( "bomb just exploded" )
}
void function BombTicker()
{
Assert( IsNewThread(), "BombTicker must be threaded off" )
wait RandomFloatRange( 3, 9 )
FlagSet( "BombHasExploded" )
}