[Tutorial] Player-timers
#1

I have seen a lot of people using the SA-MP timers for their players, which is rather redundant if you want an efficient script. There is no point in starting a new timer just to check if they're close to something every 5 seconds, or if they're using hacks every two seconds, or anything else every x seconds.

Instead, use variables to store unix time and put them in OnPlayerUpdate. Let me demonstrate:

Issue: I want a timer to check every three seconds if a player is near XYZ.
Solution:
First I need a variable to store the timer in:
pawn Code:
new XYZCheckTimer[MAX_PLAYERS];
Now I put the variable inside OnPlayerUpdate, which is called lots of times each second.
pawn Code:
public OnPlayerUpdate(playerid)
{
    new iTime = gettime();
    if(XYZCheckTimer[playerid] < iTime)
    {
        if(IsPlayerInRangeOfPoint(playerid, range, x, y, z))
        {
            do the stuff!
            XYZCheckTimer[playerid] = iTime+3;
        }
    }
    return 1;
}
Note that this causes less CPU reduction than a timer does since it only checks for a variable in a function that is already called automatically instead of creating a new process for the server to take care of.

I can even create my own "One-second timer" here:
pawn Code:
new OneSecondTimer;

public OnPlayerUpdate(playerid)
{
    new iTime = gettime();
    if(OneSecondTimer < iTime)
    {
        OneSecond();
        OneSecondTimer = iTime;
    }
    return 1;
}

stock OneSecond()
{
    One second passed!
}
(Update)
This technique can also be applied to command restrictions (And other similar player-based restrictions). Let's say we need to make sure players can't repair their cars more often than every X seconds:
pawn Code:
new aRepairTime[MAX_PLAYERS];

public OnPlayerCommandText
{
    // ... they try to repair:
    if(aRepairTime[playerid] < gettime()) // Checks if they've waited long enough since last time (Or haven't done it at all)
    {
        // Do the repair
        aRepairTime[playerid] = gettime()+X // X = Amount of seconds you want to block them
    }
}
Please note that these timers only work if there are players online and active (Not tabbed out). OnPlayerUpdate is only called when a player sends their information to the server (Position, facing angle etc), but if they're playing this happens many times each second, as I mentioned above.

Good luck!
Reply
#2

While I do not disagree with the logic here, especially for very frequent ( or short ) timers, I am sceptical as to the efficiency of putting an entire scripts worth of timers under OnPlayerUpdate. Particularly for things which might be checked every 10 minutes or more.

I would be interested to know if it would still be more efficient to check unix time vs a variable 12,000 times, rather than doing one larger computation under a repeating 10 minute timer.

To put this in perspective, in my script I often use a repeating 1 minute timer to manipulate variables that have been set according to how many minutes I want the 'cooldown' period to be. Eg If I want to block a player from repairing a vehicle for 5 minutes, I will set 'repairtime[playerid]' to 5, and decrement it under the aforementioned 1 minute timer.

I consider this to be neater, and assume it is more efficient than a new SetTimerEx("function","d",playerid); for each player.

Tell me what you think.
Reply
#3

That's quite a bad way of putting restrictions on players, timers should never be used for such things. Just put the time of their last action you want to block in a variable, then when they try to do it again check if X seconds passed since that timestamp.
pawn Code:
new repairtime[MAX_PLAYERS];

OnPlayerCommandText // or whatever function you're using it in
{
    // ... they try to do it:
    if(repairtime[playerid] < gettime()) // If they've waited long enough
    {
        // Do the repair
        repairtime[playerid] = gettime()+X // X = Amount of seconds you want to block them
    }
}
I would love to have some form of comparison between the CPU usage of the two different methods, but I can't think of any way to benchmark them properly.
Reply
#4

This is good, I can see the benefits of using gettime for this. I already use unix timestamps for quite a lot of similar applications in the script, some of which are over a much greater time period.

Assuming the result of gettime() is an integer, how large is it? can it be stored in a variable without wrapping to negative?

Does a unix timestamp ever roll around to zero? effectively causing an error in the longer time comparisons that I have. and if so how often?
[edit] ****** answered my second question
Reply
#5

As you can see on the SA-MP wiki, the function returns the amount of seconds passed since 00:00 Jan 1 1970, which is the "official" start of the Unix era. So it can never return a lower value at a later point than the first. It can very much be stored in a regular integer or array cell.
Reply
#6

Quote:
Originally Posted by Y_Less
View Post
I'm sorry, if this is more efficient that normal timers I will be VERY much surprised!

1) Normal timers are C++, these timers are PAWN - instant win for natives!

2) You are just replicating the source of normal timers in PAWN - how do you think they work? It is NOT using processes as you seem to think, it is just a function in the server called in EXACTLY the same way as you are doing, to check the elapsed time and fire off PAWN functions as appropriate.

3) This doesn't support timers with sub-second fractions.

When used properly there is nothing at all wrong with using SA:MP timers - they were designed to have many spawned, plus they keep code neat and separated, this method doesn't.
I also don't see the big 'hype' about 'timers are bad, don't use them' I've used timers quite alot, and I never had any lagg at all, that includes a timer that is called every second.

But, anyways, nice tutorial Lenny, I did learn something from it, and I am sure some more people will.
Reply
#7

How often are the timers checked?
Reply
#8

afaik OnPlayerUpdate is only run twice a second per player when on foot, which is equal to 5 milliseconds if you have 100 players active on your server (2x100 = 200 times a second = once every 5 ms right?)

But like you said, C++ wins :/
Reply
#9

Quote:
Originally Posted by Lenny the Cup
View Post
afaik OnPlayerUpdate is only run twice a second per player when on foot, which is equal to 5 milliseconds if you have 100 players active on your server (2x100 = 200 times a second = once every 5 ms right?)

But like you said, C++ wins :/
Usually ~30 times a second when moving, aiming, or driving.
Reply
#10

Most of the time you don't need timers anyway, OnPlayerUpdate is much better as it spreads out the load rather than running the code for up to 500 players in 1 go. As it stands, I'm sure you could rewrite 90% of SA:MP gamemodes with no timers at all.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)