[Tutorial] Using less timers (1 timer for everything - faster script)
#1

Hey guys! I've been doing this for quite some time now and just got the time to share it with you. Some of you guys might have been using this method for a while and some haven't, this tutorial is really to target those who haven't. Let's get started!

- How does this work?

We will execute this via the use of unix timestamps. This basically eliminates the need to use more than 1 timer for each command that requires a: refresh, reload or whatever you call it - examples: /heal, /taze, /fish...and so on.

- Coding

Before (multiple timers - old):
This is what your code normally looks like without the use of unix timestamps:
pawn Code:
//creating our variables...
new fish_reload_timer[MAX_PLAYERS], cash_reload_timer[MAX_PLAYERS];

public OnPlayerDisconnect(playerid)
{
    //killing the timers if they were created.
    if(fish_reload_timer[playerid] >= 2)KillTimer(fish_reload_timer[playerid]);
    if(cash_reload_timer[playerid] >= 2)KillTimer(cash_reload_timer[playerid]);
    return 1;
}

COMMAND:heal(playerid, params[])
{
    if(heal_reload[playerid] > 0)return SendClientMessage(playerid,0xFF0000FF,"Please wait before using this command again.");
    SetPlayerHealth(playerid,100.0);
    SendClientMessage(playerid,0xFFFFFFFF,"You've healed yourself.");
    heal_reload_timer[playerid] = SetTimerEx("HealReload",10000,false,"i",playerid);
    //creating a timer to reload the /heal command.
    return 1;
}

forward HealReload(playerid);
public HealReload(playerid)
{
    SendClientMessage(playerid,0xFFFFFFFF,"The /heal command is ready for use once more.");
    return 1;
}

COMMAND:cash(playerid, params[])
{
    if(cash_reload[playerid] > 0)return SendClientMessage(playerid,0xFF0000FF,"Please wait before using this command again.");
    SetPlayerHealth(playerid,100.0);
    SendClientMessage(playerid,0xFFFFFFFF,"You've been given $10,000 by the server.");
    cash_reload_timer[playerid] = SetTimerEx("CashReload",180000,false,"i",playerid);
    //creating YET another timer to reload the /cash command.
    return 1;
}

forward CashReload(playerid);
public CashReload(playerid)
{
    SendClientMessage(i,0xFFFFFFFF,"The /cash command is ready for use once more.");
    return 1;
}
After (single timer - new):
Now this is what it would look like after you started using unix timestamps:

pawn Code:
#include <a_samp>

/*
creating our variables...
sine we're only using 2 for this tutorial, I won't put them into an enum.
*/

new fish_reload[MAX_PLAYERS], cash_reload[MAX_PLAYERS];

public OnGameModeInit()
{
    //this is the ONE timer we'll use and it MUST be a 1 second timer...
    SetTimer("GlobalCheck",1000,true);
    return 1;
}

public OnPlayerConnect(playerid)
{
    /*
    setting the variables to their off/null state (zero) so that we won't experience any bugs later on...
    you should practice to do this with most of not all of your variables.
    */

    heal_reload[playerid] = 0;
    cash_reload[playerid] = 0;
    return 1;
}

COMMAND:heal(playerid, params[])
{
    if(heal_reload[playerid] > 0)return SendClientMessage(playerid,0xFF0000FF,"Please wait before using this command again.");
    SetPlayerHealth(playerid,100.0);
    SendClientMessage(playerid,0xFFFFFFFF,"You've healed yourself.");
    heal_reload[playerid] = gettime() + 10;
    /*
    as you can see above we used the 'gettime()' function to get the time when the command was used
    and added 10 second to that, then stored it into the 'heal_reload' variable
    which will be processed in the function called by our SINGLE timer
    */

    return 1;
}

COMMAND:cash(playerid, params[])
{
    if(cash_reload[playerid] > 0)return SendClientMessage(playerid,0xFF0000FF,"Please wait before using this command again.");
    SetPlayerHealth(playerid,100.0);
    SendClientMessage(playerid,0xFFFFFFFF,"You've been given $10,000 by the server.");
    heal_reload[playerid] = gettime() + 180;
    /*
    as you can see above we used the 'gettime()' function to get the time when the command was used
    and added 300 second (3 minutes) to that, then stored it into the 'cash_reload' variable
    which will be processed in the function called by our SINGLE timer
    */

    return 1;
}

//now down to our function...
forward GlobalCheck();
public GlobalCheck()
{
    foreach(Player,i) //or you can use a normal loop 'for(new i=0; i<MAX_PLAYERS; i++)'
    {
        /*
        if the heal_reload time is more than 0 (this means the command has been used) and
        the current time it is now, is less than the time it was used, we're gonna go on and
        allow the player to be able to use the command again
        example:
                if the command was used at 10:10:05 AM and it's now 10:10:15 AM (10s interval)
                we're gonna tell the player that they're free to use it again, therefore setting
                heal_reload to zero.
        */

        if(heal_reload[i] > 0 && gettime() <= heal_reload[i])
        {
            heal_reload[i] = 0;
            SendClientMessage(i,0xFFFFFFFF,"The /heal command is ready for use once more.");
        }
        //same thing applies for above.
        if(cash_reload[i] > 0 && gettime() <= cash_reload[i])
        {
            cash_reload[i] = 0;
            SendClientMessage(i,0xFFFFFFFF,"The /cash command is ready for use once more.");
        }
    }
    return 1;
}
- Comparison

Before (multiple timers - old)

- You might forget to kill a timer.
- More timers, the more lag.

After (single timer - new)

- No need to be killing timers.
- ONE timer, less lag.


- Conclusion

- Well now you should be able to limit the amount of timers you use in your server therefore lowering the CPU usage, which would lead to making for a faster server.

- Credits

- Credits are given receptively to who they are due.

You will still see the need to use another timer (maybe for a smooth speedometer updating)
Reply
#2

:O Thanks! I've been thinking about using this function to replace SetTimer(Ex).
Reply
#3

You could also move that script under OPU instead, that's if you really want the confirmation message.
If you didn't then you could cut out the variables heal_reload[playerid] and cash_reload[playerid] and then just compare when they use the command
Reply
#4

The confirmation messages are mandatory .
Reply
#5

My script does something similar ^^ I find it very useful.
Reply
#6

While I do support the writing of tutorials, there are some huge issues that need to be addressed here.

1. Combining all timers into one or having less timers with more code is actually probably more problematic than having more times with less code. The statement I just made is valid in most cases, and the more code and the more timers combined into one there are, the more valid it becomes. Why? Due to the way SA-MP synchronization works. And if you think about it, it'll make sense.
Reading on the subject (more specifically, "Timers are efficient" paragraph)
This is the best topic I can find for this purpose right now.

2. You describe taking advantage of timestamps, yet there's a better way to do it rather than using timers. This comes with a little downside though - you will lose the messages like "You can use command ... now.", so the point I'm making is perhaps not very useful for you.
However what you've done can simply be solved by this:
pawn Code:
COMMAND:heal(playerid, params[])
{
    if((gettime() - heal_reload[playerid]) < 10)
        return SendClientMessage(playerid, 0xFF0000FF, "Please wait before using this command again!"), true;
    heal_reload[playerid] = gettime();

    SetPlayerHealth(playerid,100.0);
    SendClientMessage(playerid,0xFFFFFFFF,"You've healed yourself.");
    return 1;
}
There! No need for a timer, basically speaking.


I seriously hope that you take note of the 1st note that I wrote, because this is a serious misunderstanding by many many scripters. And while it does not make a difference with less code and less timers combined, there will be minor lag if there's a lot of code that takes a long time to execute per iteration or just many timers combined.

Hopefully you'll revise this tutorial. Thank you.
Reply
#7

Good job tee
Reply
#8

https://sampforum.blast.hk/showthread.php?tid=254915
Reply
#9

Quote:
Originally Posted by AndreT
View Post
While I do support the writing of tutorials, there are some huge issues that need to be addressed here.

1. Combining all timers into one or having less timers with more code is actually probably more problematic than having more times with less code. The statement I just made is valid in most cases, and the more code and the more timers combined into one there are, the more valid it becomes. Why? Due to the way SA-MP synchronization works. And if you think about it, it'll make sense.
Reading on the subject (more specifically, "Timers are efficient" paragraph)
This is the best topic I can find for this purpose right now.

2. You describe taking advantage of timestamps, yet there's a better way to do it rather than using timers. This comes with a little downside though - you will lose the messages like "You can use command ... now.", so the point I'm making is perhaps not very useful for you.
However what you've done can simply be solved by this:
pawn Code:
COMMAND:heal(playerid, params[])
{
    if((gettime() - heal_reload[playerid]) < 10)
        return SendClientMessage(playerid, 0xFF0000FF, "Please wait before using this command again!"), true;
    heal_reload[playerid] = gettime();

    SetPlayerHealth(playerid,100.0);
    SendClientMessage(playerid,0xFFFFFFFF,"You've healed yourself.");
    return 1;
}
There! No need for a timer, basically speaking.


I seriously hope that you take note of the 1st note that I wrote, because this is a serious misunderstanding by many many scripters. And while it does not make a difference with less code and less timers combined, there will be minor lag if there's a lot of code that takes a long time to execute per iteration or just many timers combined.

Hopefully you'll revise this tutorial. Thank you.
While I do understand what you've said, we can do it both ways. For the commands which require an 'available notification message', we'll have to make use of the timer, for other commands which those messages aren't mandatory, we don't have to use a timer.

Quote:
Originally Posted by -Prodigy-
View Post
Quote:
Originally Posted by Tee
View Post
We will execute this via the use of unix timestamps
Quote:
Originally Posted by MP2
View Post
0 timers is better.
Meaning not sending a message to the player who used it?
Reply
#10

Quote:
Originally Posted by AndreT
View Post
pawn Code:
COMMAND:heal(playerid, params[])
{
    if((gettime() - heal_reload[playerid]) < 10)
        return SendClientMessage(playerid, 0xFF0000FF, "Please wait before using this command again!"), true;
    heal_reload[playerid] = gettime();

    SetPlayerHealth(playerid,100.0);
    SendClientMessage(playerid,0xFFFFFFFF,"You've healed yourself.");
    return 1;
}
You can also use this way.

pawn Code:
COMMAND:heal(playerid, params[])
{
    if(heal_reload[playerid]) >= gettime()) return SendClientMessage(playerid, 0xFF0000FF, "Please wait before using this command again!"), true;
    heal_reload[playerid] = gettime() + 10;

    SetPlayerHealth(playerid,100.0);
    SendClientMessage(playerid,0xFFFFFFFF,"You've healed yourself.");
    return 1;
}
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)