[Include] y_timers - Simple timer creation and load balancing.
#1

y_timers
Introduction

This include allows you to quickly and easily define timer functions, that is functions which are to be called after a given time. The library provides two systems: tasks, which are functions that run all the time in the background (for example streamers); and timers, which are functions you can start, stop, and call on a delay at will.

Код:
#include <a_samp>

forward RepeatingTimer();
forward DelayedTimer(playerid);

public RepeatingTimer()
{
    printf("Called every N seconds.");
}

public DelayedTimer(playerid)
{
    printf("May be called after N seconds");
}

main()
{
    SetTimer("RepeatingTimer", 1000, 1);
    SetTimerEx("DelayedTimer", 500, 0, "i", 42);
}
Код:
#include <YSI\y_timers>

task RepeatingTimer[1000]()
{
    printf("Called every 1 seconds.");
}

timer DelayedTimer[500](playerid)
{
    printf("May be called after 0.5 seconds");
}

main()
{
    defer DelayedTimer(42);
}
I don't know about you, but I think the second version looks much better. There are many advantages to this method:
  • Calling conventions are defined at the function itself - when you write a function you can define how it is called.
  • Times are defined with functions, so you don't have half of a function's information in one place, and more elsewhere.
  • Tasks with the same delays are automatically balanced to not occur at the same time. For example if you have three with a one second period, they will be called a third of a second apart.
  • Changing a function to a timer function no longer requires you to modify all the calls to it.
  • The compiler can check function parameters and function names are correct.
Download

This library is a part of YSI, which can be found here. Keep your eye on that topic and your server log for updates.

YSI Download Topic

Tasks

Tasks are functions that are called constantly at a given period. Defining these is now VERY simple:

Код:
#include <YSI\y_timers>

task RepeatingFunction1[1000]()
{
    printf("Called every second, but not at the same time as RepeatingFunction2.");
}

task RepeatingFunction2[1000]()
{
    printf("Called every second, but not at the same time as RepeatingFunction1.");
}

ptask RepeatingFunction3[500](playerid)
{
    printf("Called every 500ms PER PLAYER (balanced internally).");
}
That is literally it. The full format is:

Код:
task FUNCTION_NAME[DELAY]()
{
    CODE();
}

ptask FUNCTION_NAME[DELAY](playerid)
{
    CODE();
}
A task is called after the given delay constantly, so "RepeatingFunction1" above will be called every second for as long as the server is running. "RepeatingFunction3" above will be called twice a second for every player on the server for as long as a player is connected. No players means no timers running, seven players means seven timers running*.

* Note that internally it might not be seven REAL timers, it just means that the function will be called seven times every half second, the system load-balances internally.
  • Calling
If you need to, you can still call timer functions directly:

Код:
#include <YSI\y_timers>

task RepeatingFunction[1000]()
{
    printf("Called every second, and when called explicitly.");
}

main()
{
    RepeatingFunction();
}
  • Notes
Tasks cannot have parameters (except "playerid" to ptasks) and the compiler will give errors if you try to add them (or more accurately, will give errors if you try use those parameters). This is because they are managed automatically and thus the system doesn't know what data you want to pass to them (and the data would be constant anyway).

The balancing algorithm is not perfect. If you have one timer at 400ms and one at 700ms, they will conflict once every 2.8s. This currently only offsets timers with the same periods, however cross-period-collisions are much rarer so not an issue most of the time.

Timers

Timers (as ever) allow you to call functions after a given time, without needing to mess about with "SetTimerEx":

Код:
#include <YSI\y_timers>

timer DelayedTimer[500](playerid)
{
    printf("May be called after 0.5 seconds.");
}

main()
{
    defer DelayedTimer(42);
}
That will call "DelayedTimer" after 500ms, passing the value 42. The full format of the definition is:

Код:
timer FUNCTION_NAME[DELAY](PARAMETERS)
{
    CODE();
}
The delay parameter can be anything that becomes a number - a macro sum, a constant, or even something using one of the parameters to the function:


Код:
timer Timer1[6 * 4]()
{
}

timer Timer2[something](something)
{
}

#define TIME 1000

timer Timer3[TIME]()
{
}
These rules (except for using parameters) apply to tasks as well.
  • Calling
There are three ways of calling timer functions:
  • defer

    This calls the function once after the time defined on the function:

    Код:
    main()
    {
        defer DelayedFunction(42);
    }
  • repeat

    This calls the timer repeatedly after the delay defined on the timer. This is similar to tasks but you can stop the timer again:

    Код:
    main()
    {
        repeat DelayedFunction(42);
        new
            Timer:x = repeat DelayedFunction(42);
        stop x;
    }
  • Normal call

    This simply calls the function instantly like a normal function:

    Код:
    main()
    {
        DelayedFunction(42);
    }
  • Control
The "repeat" example above gave one more little command: "stop". This, quite simply, stops a running timer.
  • Overrides
The two timer calls above can have their times changed from the defaults:

Код:
#include <YSI\y_timers>

timer DelayedFunction[1000](playerid)
{
    printf("USUALLY called after 1 second.");
}

main()
{
    // Called instantly.
    DelayedFunction(42);
    // Called in 1 second (default).
    defer DelayedFunction(42);
    // Called in 200 ms (override).
    defer DelayedFunction[200](42);
    // Called every second (default).
    repeat DelayedFunction(100);
    // Called every 7 seconds (override).
    repeat DelayedFunction[7 * 1000](100);
}
  • Arrays and Strings
Due to a bug in SetTimerEx you can not pass strings or arrays to delayed functions. I will admit this is my fault as I'm the one who wrote SetTimerEx in the first place, but it's not the fault of this library. However, this problem is now fixed, for both strings and arrays, when using y_timers:

Код:
#include <YSI\y_timers>

// Arrays must ALWAYS be followed by their length.
// ALWAYS!
timer ArrayTimer[500](arr[], len)
{
    printf("Array (%d): %d, %d, ...", len, arr[0], arr[1]);
}

// Strings DO NOT need to be followed by their length, but MUST have
// a tag of "string:" (don't worry about tag mis-matches though).
timer StringTimer[500](string:str[])
{
    printf("String: %s", str);
}

main()
{
    new
        array[3] = {42, 43, 44};
    defer ArrayTimer(array, sizeof (array));
    defer StringTimer("Hi there");
}
Credits
I am reposting this include (made by ******) with the thought of what he said to several members of the community. Everything that could help someone should not be deleted from the forums, which is something I agree with since I learned a lot by reading the tutorials on here. He made a lot of things that are used by lots of servers and that knowledge should not be lost for present and future developers.
Reply
#2

Thanks for the repost. Was waiting for this: )
Reply
#3

How come the timer function line can only be 71 symbols?

For example.
Код:
timer ATimerFunction[10000](parameter, parameter2, parameter3, paramet) // This will work just fine
timer ATimerFunction[10000](parameter, parameter2, parameter3, paramete) // This will cause the compiler to never stop running.
I guess it's some kind of a macro loop?
Reply
#4

Yup
pawn Код:
timer wat[300](wut)

//Then
#define timer YSI_timer
//(...)
#define YSI_timer%0[%1]%3(%2) stock%0_yT@(%2)return _Timer_C(O@(#%0@_yT,(I@==-1)?(%1):I@,J@,_YT@CR:%2,,)0>%0|||%0|||(%2)

//Few steps later looks like
stock wat_yT@(wut)return _Timer_C(O@(# wat@_yT,(I@==-1)?(300):I@,J@,@Yf:@Yg:@Yf:@Yg:@Yh:#id#,J@,wut),0);@Yj: wat@_yT(__r,wut);public @Yj: wat@_yT(__r,wut){ wat(wut);} wat(wut)
Reply
#5

Or you can use zeex compiler patch https://github.com/Zeex/pawn - it increase char limit from 512 to 4096
Reply
#6

Does anyone know how I can stop per-player timers?

I've tried to do something like this:
Код:
new Timer:TutorialTimer[MAX_PLAYERS];

TutorialTimer[playerid] = defer Tutorial_2(playerid);

timer Tutorial_2[20000](playerid)
{
	//stuff
        return 1;
}

//then if I wanna stop it prematurely
stop TutorialTimer[playerid];
This doesn't seem to be working
Reply
#7

y_timers uses "normal" PAWN functions to manage timers, so stop is in fact macro for KillTimer. Does normal KillTimer on nonrepeating timer stop its execution? Check it out, if it does in fact stop that timer, please create a ticket on github ysi repo.
Reply
#8

pawn Код:
enum E_RANDOM_ENUM
{
    FirstElement,
    SecondElement,
};

defer ATimerFunction(42, FirstElement);

timer ATimerFunction[1000](argument, E_RANDOM_ENUM:argument2)
{
   
}
And this results in a tag mismatch warning on "timer ATimerFunction.." line.
Reply
#9

The server crash using "repeat" it's annoying!
Reply
#10

Quote:
Originally Posted by Adoniiz
Посмотреть сообщение
The server crash using "repeat" it's annoying!
If you want a sollution you have to give us more info... Download crashdetect plugin, show us the stacktrace and the related code.
Reply
#11

Quote:
Originally Posted by dusk
Посмотреть сообщение
If you want a sollution you have to give us more info... Download crashdetect plugin, show us the stacktrace and the related code.
pawn Код:
[19:20:29] [connection] XXXX requests connection cookie.
[19:20:34] [connection] XXXX requests connection cookie.
[19:21:00] Kicking XXXX because they didn't logon to the game.
[19:21:05] Kicking XXXX because they didn'
t logon to the game.
[19:21:25] [connection] XXXX requests connection cookie.
[19:21:31] [connection] XXXX requests connection cookie.
[19:21:56] Kicking XXXX because they didn't logon to the game.
[19:22:02] Kicking XXXX because they didn'
t logon to the game.
[19:22:03] [connection] XXXX requests connection cookie.
[19:22:34] Kicking XXXX because they didn't logon to the game.
[19:22:50] [connection] XXXX requests connection cookie.
[19:23:13] [connection] XXXX requests connection cookie.
[19:23:18] [connection] XXXX requests connection cookie.
[19:23:21] Kicking XXXX because they didn'
t logon to the game.
[19:23:30] [connection] XXXX requests connection cookie.
[19:23:30] [debug] Run time error 5: "Invalid memory access"
[19:23:30] [debug] AMX backtrace:
[19:23:30] [debug] #0 00018b88 in Iter_Add_InternalC (&count=@00011ed0 180, array[]=@0000ff88 "", size=2002, value=207) at X:\direccion\direccion\direccion\pawno\include\YSI\..\YSI_Coding\..\YSI_Data\y_foreach/impl.inc:950
[19:23:30] [debug] #1 0003f12c in Iter_VehicleDo (bool:add=true, vehicleid=207) at X:\direccion\direccion\direccion\pawno\include\YSI\..\YSI_Coding\..\YSI_Data\y_foreach/iterators.inc:647
[19:23:30] [debug] #2 0003f0c4 in public Iter_VehicleDo@ (bool:add=true, vehicleid=207, __m=-1) at X:\direccion\direccion\direccion\pawno\include\YSI\..\YSI_Coding\..\YSI_Data\y_foreach/iterators.inc:642
[19:23:30] [debug] #3 native CallRemoteFunction () from samp03svr
[19:23:30] [debug] #4 0003efec in Iter_VehicleDo_ (bool:add=true, vehicleid=207) at X:\direccion\direccion\direccion\pawno\include\YSI\..\YSI_Coding\..\YSI_Data\y_foreach/iterators.inc:642
[19:23:30] [debug] #5 0003f26c in Iter_CreateVehicle (modelid=490, Float:x=290.85168, Float:y=2507.35083, Float:z=16.48438, Float:angle=150.61819, color1=-1, color2=-1, respawn_delay=-1, addsiren=0) at X:\direccion\direccion\direccion\pawno\include\YSI\..\YSI_Coding\..\YSI_Data\y_foreach/iterators.inc:663
it's a crash random, but always crash by repeat timers (y_timers), where into the code, there loops of y_iterate.

I put YSI of 18th Feb 2015, and until now, the server don't crash.
Reply
#12

I can't create a ptask. Any suggestion?
Quote:

Carabines.pwn(1284) : error 047: array sizes do not match, or destination array is too small

Quote:

ptask PLAYERTASK[1000](playerid)
{
if(IsPlayerInAnyVehicle(playerid))
{
new string[4];
format(string, sizeof(string), "%d", GetPlayerSpeed(playerid));
PlayerTextDrawSetString(playerid, VEHICLE_SPEED_TEXTDRAW[playerid], string);
}
}

Reply
#13

Quote:
Originally Posted by MP2
Посмотреть сообщение
From ******:
Quote:

That error usually occurs when you change "MAX_PLAYERS" after including YSI instead of before, meaning that the different parts of your mode are using different values.

Always re-define MAX_PLAYERS like so:

[pawn]
#include <a_samp>
#undef MAX_PLAYERS
#define MAX_PLAYERS 123

// rest of script
[/[pawn]
SOLVED
Reply
#14

What is the correct way of checking that a timer is active?

After doing
Код:
Player[playerid][LoginTimer] = defer OnLoginTimeout(playerid);
Would the following be correct?
Код:
if (Player[playerid][LoginTimer])
{
	// Timer active
}
else
{
	// Timer not active
}
Also when I want to kill the timer:
Код:
stop Player[playerid][LoginTimer]; // is this enough?
Player[playerid][LoginTimer] = 0; // or do I also have to do this?
Reply
#15

Quote:
Originally Posted by Dodo9655
Посмотреть сообщение
What is the correct way of checking that a timer is active?

After doing
Код:
Player[playerid][LoginTimer] = defer OnLoginTimeout(playerid);
Would the following be correct?
Код:
if (Player[playerid][LoginTimer])
{
	// Timer active
}
else
{
	// Timer not active
}
Also when I want to kill the timer:
Код:
stop Player[playerid][LoginTimer]; // is this enough?
Player[playerid][LoginTimer] = 0; // or do I also have to do this?
In the first place, excuse me for bumping this thread! As I'm working with y_timers right now, let me answer this question for the next generation:
PHP код:
/* Set all cells to -1 */
new Timer:myTimer[MAX_PLAYERS] = {Timer:-1, ...};
/* Start the timer */
CMD:mycommand(playeridparams[])
{
    
myTimer[playerid] = repeat MyOwnFunction(playerid);
    
/* MyOwnFunction will be called 1 second after we type the
    command, due the delay parameter from timer, that's how it works. */
    
return 1;
}
/* Declare MyOwnFunction as a timer */
timer MyOwnFunction[1000](playerid)
{
    
printf("The player who started timer %d has the ID %d."_:myTimer[playerid], playerid);
    
// This function will print the same message every second.
}
/* Stop the timer */
COMMAND:stoptimer(playeridparams[])
{
    if(
myTimer[playerid] != Timer:-1)
    {
        
stop myTimer[playerid];
        
myTimer[playerid] = Timer:-1;
    }
    else
    {
        
// The timer isn't running.
    
}
    return 
1;

Reply
#16

I'm not home or I'd test this, does anyone know if you can defer a ptask with the playerid parameter?

Like:
pawn Код:
ptask PlayerCheck[1000](playerid) {}

defer PlayerCheck[50](playerid);
There's nothing in the tests code on this.

I think it'll work the same as a 'timer', just asking to be sure.
Reply
#17

I have encountered an issue with passing on strings.

Calling the timer
Код:
defer SpecialNumberNextQuestion(playerid, "test");
Timer's declaration
Код:
timer SpecialNumberNextQuestion[1000](playerid, string:text[])
{
	printf("Text printed: %s", text);
	SendClientMessage(playerid, COLOR_YELLOW, text);
}
When printed in-game, it just prints blank and in the console it prints out the few last characters that I printed out because it is called from OnPlayerText:


I tried to rename the variable in the timer to something else other than "text" since it seemed like it was referencing to the OnPlayerText(playerid, text[]) this text variable name but it didn't help either.

Any ideas of what I might be doing wrong?
Reply
#18

Quote:
Originally Posted by RIDE2DAY
Посмотреть сообщение
In the first place, excuse me for bumping this thread! As I'm working with y_timers right now, let me answer this question for the next generation:
PHP код:
/* Set all cells to -1 */
new Timer:myTimer[MAX_PLAYERS] = {Timer:-1, ...};
/* Start the timer */
CMD:mycommand(playeridparams[])
{
    
myTimer[playerid] = repeat MyOwnFunction(playerid);
    
/* MyOwnFunction will be called 1 second after we type the
    command, due the delay parameter from timer, that's how it works. */
    
return 1;
}
/* Declare MyOwnFunction as a timer */
timer MyOwnFunction[1000](playerid)
{
    
printf("The player who started timer %d has the ID %d."_:myTimer[playerid], playerid);
    
// This function will print the same message every second.
}
/* Stop the timer */
COMMAND:stoptimer(playeridparams[])
{
    if(
myTimer[playerid] != Timer:-1)
    {
        
stop myTimer[playerid];
        
myTimer[playerid] = Timer:-1;
    }
    else
    {
        
// The timer isn't running.
    
}
    return 
1;

Thank you for that very helpful tip!
Reply
#19

Is it possible to hook a repeating task with y_hooks?
Reply
#20

@wallee I'm afraid not yet (see this). The JIT ****** mentioned is still in the works, and neither of us has now time to fix it in old version as well. Sorry
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)