Difference between revisions of "Timers (SourceMod Scripting)"
Peace-Maker (talk | contribs) |
Peace-Maker (talk | contribs) |
||
Line 12: | Line 12: | ||
<pawn>public OnPluginStart() | <pawn>public OnPluginStart() | ||
{ | { | ||
− | CreateTimer(5.0, LoadStuff) | + | CreateTimer(5.0, LoadStuff); |
} | } | ||
public Action:LoadStuff(Handle:timer) | public Action:LoadStuff(Handle:timer) | ||
{ | { | ||
− | PrintToServer("Loading stuff!") | + | PrintToServer("Loading stuff!"); |
}</pawn> | }</pawn> | ||
Line 55: | Line 55: | ||
<pawn> | <pawn> | ||
− | new Handle:WelcomeTimers[MAXPLAYERS+1] | + | new Handle:WelcomeTimers[MAXPLAYERS+1]; |
public OnClientPutInServer(client) | public OnClientPutInServer(client) | ||
{ | { | ||
− | WelcomeTimers[client] = CreateTimer(15.0, WelcomePlayer, client) | + | WelcomeTimers[client] = CreateTimer(15.0, WelcomePlayer, client); |
} | } | ||
Line 66: | Line 66: | ||
if (WelcomeTimers[client] != INVALID_HANDLE) | if (WelcomeTimers[client] != INVALID_HANDLE) | ||
{ | { | ||
− | KillTimer(WelcomeTimers[client]) | + | KillTimer(WelcomeTimers[client]); |
− | WelcomeTimers[client] = INVALID_HANDLE | + | WelcomeTimers[client] = INVALID_HANDLE; |
} | } | ||
} | } | ||
Line 73: | Line 73: | ||
public Action:WelcomePlayer(Handle:timer, any:client) | public Action:WelcomePlayer(Handle:timer, any:client) | ||
{ | { | ||
− | PrintToConsole(client, "Welcome to the server!") | + | PrintToConsole(client, "Welcome to the server!"); |
− | WelcomeTimers[client] = INVALID_HANDLE | + | WelcomeTimers[client] = INVALID_HANDLE; |
}</pawn> | }</pawn> | ||
Line 82: | Line 82: | ||
public OnClientPutInServer(client) | public OnClientPutInServer(client) | ||
{ | { | ||
− | CreateTimer(15.0, WelcomePlayer, GetClientSerial(client)) // You could also use GetClientUserId(client) | + | CreateTimer(15.0, WelcomePlayer, GetClientSerial(client)); // You could also use GetClientUserId(client) |
} | } | ||
public Action:WelcomePlayer(Handle:timer, any:serial) | public Action:WelcomePlayer(Handle:timer, any:serial) | ||
{ | { | ||
− | new client = GetClientFromSerial(serial) // Validate the client serial | + | new client = GetClientFromSerial(serial); // Validate the client serial |
if (client == 0) // The serial is no longer valid, the player must have disconnected | if (client == 0) // The serial is no longer valid, the player must have disconnected | ||
{ | { | ||
− | return | + | return Plugin_Stop; |
} | } | ||
− | PrintToConsole(client, "Welcome to the server!") | + | PrintToConsole(client, "Welcome to the server!"); |
}</pawn> | }</pawn> | ||
If you want to close or cancel a timer when a client disconnects, then use the first example with handles. | If you want to close or cancel a timer when a client disconnects, then use the first example with handles. | ||
Line 106: | Line 106: | ||
The above example could be rewritten as: | The above example could be rewritten as: | ||
<pawn> | <pawn> | ||
− | new Handle:WelcomeTimers[MAXPLAYERS+1] | + | new Handle:WelcomeTimers[MAXPLAYERS+1]; |
public OnClientPutInServer(client) | public OnClientPutInServer(client) | ||
{ | { | ||
− | new Handle:pack | + | new Handle:pack; |
− | WelcomeTimers[client] = CreateDataTimer(15.0, WelcomePlayer, pack) | + | WelcomeTimers[client] = CreateDataTimer(15.0, WelcomePlayer, pack); |
− | WritePackCell(pack, client) | + | WritePackCell(pack, client); |
− | WritePackString(pack, "Welcome to the server!") | + | WritePackString(pack, "Welcome to the server!"); |
} | } | ||
Line 120: | Line 120: | ||
if (WelcomeTimers[client] != INVALID_HANDLE) | if (WelcomeTimers[client] != INVALID_HANDLE) | ||
{ | { | ||
− | KillTimer(WelcomeTimers[client]) | + | KillTimer(WelcomeTimers[client]); |
− | WelcomeTimers[client] = INVALID_HANDLE | + | WelcomeTimers[client] = INVALID_HANDLE; |
} | } | ||
} | } | ||
Line 127: | Line 127: | ||
public Action:WelcomePlayer(Handle:timer, Handle:pack) | public Action:WelcomePlayer(Handle:timer, Handle:pack) | ||
{ | { | ||
− | decl String:str[128] | + | decl String:str[128]; |
− | new client | + | new client; |
/* Set to the beginning and unpack it */ | /* Set to the beginning and unpack it */ | ||
− | ResetPack(pack) | + | ResetPack(pack); |
− | client = ReadPackCell(pack) | + | client = ReadPackCell(pack); |
− | ReadPackString(pack, str, sizeof(str)) | + | ReadPackString(pack, str, sizeof(str)); |
− | PrintToConsole(client, "%s", str) | + | PrintToConsole(client, "%s", str); |
− | WelcomeTimers[client] = INVALID_HANDLE | + | WelcomeTimers[client] = INVALID_HANDLE; |
}</pawn> | }</pawn> | ||
Line 155: | Line 155: | ||
*Execution of a one-time timer finishes. | *Execution of a one-time timer finishes. | ||
− | When a timer dies, if <tt> | + | When a timer dies, if <tt>TIMER_DATA_HNDL_CLOSE</tt> is set, the Handle will always be closed with the permissions that <tt>CloseHandle()</tt> uses by default. Since timers cannot be cloned, there should be no ownership issues. |
==Changing Intervals== | ==Changing Intervals== |
Revision as of 08:23, 10 February 2014
Timers in SourceMod are timed events that occur once or repeatedly at a given interval.
Contents
Introduction
Timers allow you to set an interval, a function to use as the event callback, and an optional Handle to pass through the callback. This is useful for saving data asynchronously.
All timer functions are in plugins/include/timers.inc.
Basic Usage
One-Time Timers
One-time timers are timers that only execute once. An example of a one-time timer might look like:
public OnPluginStart() { CreateTimer(5.0, LoadStuff); } public Action:LoadStuff(Handle:timer) { PrintToServer("Loading stuff!"); }
This code will print "Loading stuff!" to the server console five seconds after the map loads. The return value has no meaning for one-time timers.
Repeatable Timers
Repeatable timers execute infinitely many times, once every interval. Based on the return value, they either continue or cancel.
For example, say you want to display a message five times, once every three seconds:
someFunction() { CreateTimer(3.0, Timer_PrintMessageFiveTimes, _, TIMER_REPEAT); } public Action:Timer_PrintMessageFiveTimes(Handle:timer) { // Create a global variable visible only in the local scope (this function). static numPrinted = 0; if (numPrinted >= 5) { numPrinted = 0; return Plugin_Stop; } PrintToServer("Warning! This is a message."); numPrinted++; return Plugin_Continue; }
Note that Plugin_Stop stops the timer, and Plugin_Continue allows it to continue repeating.
Passing Data
Simple Values
As mentioned earlier, timers let you pass values on to the callback function. This value can be any type. For example, let's say we want to print a message to a player fifteen seconds after they connect. However, we want to cancel the timer if the player disconnects. Implementing this is very easy:
new Handle:WelcomeTimers[MAXPLAYERS+1]; public OnClientPutInServer(client) { WelcomeTimers[client] = CreateTimer(15.0, WelcomePlayer, client); } public OnClientDisconnect(client) { if (WelcomeTimers[client] != INVALID_HANDLE) { KillTimer(WelcomeTimers[client]); WelcomeTimers[client] = INVALID_HANDLE; } } public Action:WelcomePlayer(Handle:timer, any:client) { PrintToConsole(client, "Welcome to the server!"); WelcomeTimers[client] = INVALID_HANDLE; }
Another example without using handles is to use either UserID or ClientSerial (which is unique to each player):
public OnClientPutInServer(client) { CreateTimer(15.0, WelcomePlayer, GetClientSerial(client)); // You could also use GetClientUserId(client) } public Action:WelcomePlayer(Handle:timer, any:serial) { new client = GetClientFromSerial(serial); // Validate the client serial if (client == 0) // The serial is no longer valid, the player must have disconnected { return Plugin_Stop; } PrintToConsole(client, "Welcome to the server!"); }
If you want to close or cancel a timer when a client disconnects, then use the first example with handles.
Handles
If you want to pass a Handle as a value, you have the option of using TIMER_DATA_HNDL_CLOSE, which will automatically call CloseHandle() for you once the timer dies.
Data Packs
Data packs are packable structures that can be used to hold asynchronous data (data that must be saved and unpacked for later). They are especially useful for timers, and thus there exists a helper function, called CreateDataTimer(), which creates a timer using a data pack handle. The handle is created and closed automatically for you.
The above example could be rewritten as:
new Handle:WelcomeTimers[MAXPLAYERS+1]; public OnClientPutInServer(client) { new Handle:pack; WelcomeTimers[client] = CreateDataTimer(15.0, WelcomePlayer, pack); WritePackCell(pack, client); WritePackString(pack, "Welcome to the server!"); } public OnClientDisconnect(client) { if (WelcomeTimers[client] != INVALID_HANDLE) { KillTimer(WelcomeTimers[client]); WelcomeTimers[client] = INVALID_HANDLE; } } public Action:WelcomePlayer(Handle:timer, Handle:pack) { decl String:str[128]; new client; /* Set to the beginning and unpack it */ ResetPack(pack); client = ReadPackCell(pack); ReadPackString(pack, str, sizeof(str)); PrintToConsole(client, "%s", str); WelcomeTimers[client] = INVALID_HANDLE; }
Notes
Accuracy
The smallest possible interval is 0.1 seconds. Timers have high precision (floating point) but low accuracy, as the current time is based on the in-game tick count, not the system clock. This has two implications:
- If the server is paused (not ticking or hibernating), timers will not run.
- The server will not always tick at an exact interval.
For example, a 1.234 second interval timer starting from time t might not tick until t+1.242 at a tickrate of 66 ticks per second.
Timer Death
All timers are guaranteed to die either by:
- CloseHandle() being called (or on plugin unload);
- KillTimer() being called;
- Plugin_Stop being returned from a repeatable timer;
- TriggerTimer() being called on a one-time timer;
- Execution of a one-time timer finishes.
When a timer dies, if TIMER_DATA_HNDL_CLOSE is set, the Handle will always be closed with the permissions that CloseHandle() uses by default. Since timers cannot be cloned, there should be no ownership issues.
Changing Intervals
The interval of a timer cannot be changed as of this writing. The timer must be killed and a new one created.
AMX Mod X
Unlike AMX Mod X's set_task function, you cannot pass arrays using CreateTimer. To accomplish this, you should use the data pack functionality explained above.
Similarly, there are no flags for counted repeats (non-infinite loops) or timers based on the map start or end. These must be done manually.
External Links
- On Timer Design, Part 3 (SourceMod DevLog)
- On Timer Design, Part 2 (SourceMod DevLog)
- On Timer Design, Part 1 (SourceMod DevLog)
Warning: This template (and by extension, language format) should not be used, any pages using it should be switched to Template:Languages | |
View this page in: English Russian 简体中文(Simplified Chinese) |