SA-MP Forums Archive
[Tutorial] To optimize our GM and our Server improves. - Printable Version

+- SA-MP Forums Archive (https://sampforum.blast.hk)
+-- Forum: SA-MP Scripting and Plugins (https://sampforum.blast.hk/forumdisplay.php?fid=8)
+--- Forum: Scripting Help (https://sampforum.blast.hk/forumdisplay.php?fid=12)
+---- Forum: Tutorials (https://sampforum.blast.hk/forumdisplay.php?fid=70)
+---- Thread: [Tutorial] To optimize our GM and our Server improves. (/showthread.php?tid=482840)

Pages: 1 2


To optimize our GM and our Server improves. - Swedky - 23.12.2013

Well, these are some forms that I know to optimize our GM, and clearly, to improve the server.
We will begin with the inconveniences that the Users have:




Timer's:
Something that always the Ping raises in our server. I will give them forms of how solving it.



Timer for restrictions.


If we use Timer's for restrictions, I refer that a user could not use a command for 'X' time, a Timer is the last thing that they should use. One of the best methods is to use the include created by so called DesingMyCry: a_tiempos.inc - he handles your times to whim. It is %100 effective and it will help us very much to remove unnecessary Timer's since it uses the SA-MP's native ones. ('Gettime').

Since the tutorial is in Spanish and his codes also, I it will translate to the Englishman in order that they deal:


pawn Code:
#define SecondsToMinutes(%1) ((%1)*(60))
#define SecondToHour(%1) (((%1)*(60))*(60))
#define SecondToDay(%1) ((((%1)*(24))*(60))*(60))


stock Timer:operator = (t) return Timer:t;


stock StartTimer(&Timer:timer, const TimerTime)
{
    timer = ((gettime())+(TimerTime));
    return 1;
}
stock ObtainTimer(Timer:timer)
{
    new CurrentTime = gettime();
    return (_:timer-CurrentTime  <= 0) ? (0) : (_:timer-CurrentTime);
}

stock bool:TimerHasHappened(Timer:timer)
{
    return (ObtainTimer(_:timer) <= 0) ? (true) : (false);
}
Good, I will leave an example them of since using it:

pawn Code:
#include <a_samp>
#include <a_timers> // Here the name of the include.

new Timer:RestrictionCMD[MAX_PLAYERS];

public OnPlayerCommandText(playerid, cmdtext[])
{
      //if(TimerHasHappened(RestrictionCMD[playerid]) == true)
    if(TimerHasHappened(RestrictionCMD[playerid]))
    {
        StartTime(RestrictionCMD[playerid], SecondsToMinute(5));
        return SendClientMessage(playerid, -1, "It used the command correctly. Now it waits 5 minutes to return to use any other one.");
    }
    else
    {
        new string[40];
        format(strimg, sizeof(string), "Wait %i seconds before using a command.", ObtainTimer(RestrictionCMD[playerid]));
        SendClientMessage(playerid, -1, string);
    }
    return 0;
}
StarTimer: It begins the restriction.
ObtainTimer: It obtains the quantity of time that is absent in order that it finishes the timer.
TimerHasHappened: It detects if the Timer already happened.



Gratings.

It is strange that a GM does not use gratings and/or inner doors in movement. Mainly, to close the grating they use a Timer, but there is another method: https://sampwiki.blast.hk/wiki/OnObjectMoved - It is called when an object stops moving.



Cells.

Probably some persons format many cells unnecessarily, I refer that they put more than it will be formatted.



Example:



THIS IS NOT RECOMMENDED:

pawn Code:
new string[144];
format(string, sizeof(string), "* Your principal information: %s[%d].", GetName(playerid), playerid);
SendClientMessage(playerid, -1, string);

BUT YES THIS:

pawn Code:
new string[61]; /* *Thanks to Bark for the notice.* */
format(string, sizeof(string), "* Your principal information: %s[%d].", GetName(playerid), playerid);
SendClientMessage(playerid, -1, string);

What did I do there? It was to lower the cells and to use only the necessary ones.

Note: Bear in mind that if they format the name of the player, they must add '+24' cells to the array.
Note 2: The Hexadecimal colors also will store '+8' cells. (Writing the entire hexadecimal).
Note 3: Remember '+1' use cell for the void character.




When to use static and when to use new?.


What I want to say, is that we must use static only global type, and new only local type, and they will wonder: why?. Simply because when the server initiates, in static the variable/array is created only once until you close the server, besides whom this one spends fewer resources and there is no need to global type be creating it again and again like in case of new.
And we use
new locally, since on having closed the keys it resigns of the memory.

Example:


pawn Code:
static Team[MAX_PLAYERS]; // We declare that we will use this array in the whole GM.

public OnPlayerSpawn(playerid)
{
    new string[70]; // We declare that this array will be used in the whole callack.
    format(string, sizeof(string), "* %s[%d] It appeared and it is ready to play.", GetName(playerid), playerid);
    SendClientMessageToAll(0xF10000FF, string);

    if(Team[playerid] > 0)
    {
        new string2[39]; // We declare that we will use this array only in conditional this one.
        format(string2, sizeof(string2), "* You are of the Equipment number: %i.", Team[playerid]);
        SendClientMessageToAll(0xF10000FF, string2);
    } // We close the keys and erase the array 'string2' of the memory.
    return 1;
} // We close the keys of the callback and erase the array 'string' of the memory.



To optimize codes.


Some of the examples that it could give, it is of that the persons use often the conditional ones, instead of using switch.
For example:

INCORRECT:

pawn Code:
stock ProvedAtRandom()
{
    new Random = random(3);

    if(random == 0)
    {
        SendRconCommand("gmx");
    }
    else if(random == 1)
    {
        SendRconCommand("pasword Tutorial.");
    }
    else if(random == 2)
    {
        SendRconCommand("reloadfs Tutorial");
    }
}
CORRECT:

pawn Code:
stock ProvedAtRandom()
{
    new Random = random(3);
    switch(Random)
    {
        case 0: SendRconCommand("gmx");
        case 1: SendRconCommand("password Tutorial.");
        case 2: SendRconCommand("reloadfs Tutorial");
    }
    return Random;
}
It would return the value 'Random' and our code would be more optimized, besides that switch always reads directly the codes that it uses in of his keys, whereas if verifies one for one.
return can return binary, entire numbers/decimals, chains of text, etc.


Another example is:


pawn Code:
stock GetName(playerid) // Code used in previous examples in this tutorial.
{
    new Nick[MAX_PLAYER_NAME];
    GetPlayerName(playerid, Nick, sizeof(Nick));
    return Nick; // He returns a chain of text.
}
It notices: Some stock's sometimes must not return at least that we put a value. (Also the callback's that we create).



Processors of Commands.
Always commands are used in our Server, and a way of optimizing our GM is to use a processor of Commands, more acquaintances it is ZCMD for Zeex and Y_Commands for ******.

Y_Commands - https://sampforum.blast.hk/showthread.php?tid=169029.
ZCMD - https://sampforum.blast.hk/showthread.php?tid=91354.



Others.


Some of them use 500 cells for which they use the array with the constant 'MAX_PLAYERS', being that our server not reaches not even 50/70 users.
We must use the necessary, at the time quantity, what we must do? To go below of the included bookshop '
a_samp' and to re-define the above mentioned constant; therefore, it would be like that:

pawn Code:
#include <a_samp>
#undef MAX_PLAYERS
#define MAX_PLAYERS Quantity // We change 'quantity' into the quantity of slot's that you use in your Server.



Loop's.
Always we create curls, already be for all the players or to repeat 'X' code.
When we use a curl for all the players, the best option is to use '
foreach' for ******. What this include does is to create a curl only for the connected players, and is %100 exactly.

I will leave the link them of of the same one:
[Include] foreach - 0.3 compatible - updated 19/12/12.




_____________________


Well, it was the whole tutorial.
I am new doing tutoriales, if they have some doubt or I was wrong in something, please, warn me.
Excuse the evil Englishman, I am of the Spanish section and do not remove very well with the Englishman that we say.

Regards.


Re: To optimize our GM and our Server improves. - nGen.SoNNy - 23.12.2013

Thanks for this tutorial. I hope that most of our "scripters" will get from tips from this post.


Re: To optimize our GM and our Server improves. - Emmet_ - 23.12.2013

Nice tutorial.

About this:

pawn Code:
stock ProvedAtRandom()
{
    new Random = random(3);
    switch(Random)
    {
        case 0: SendRconCommand("gmx");
        case 1: SendRconCommand("password Tutorial.");
        case 2: SendRconCommand("reloadfs Tutorial");
    }
    return Random;
}
I generally tend to do this:

pawn Code:
stock ProvedAtRandom()
{
    static const g_Commands[][] =
    {
        {"gmx"},
        {"password Tutorial."},
        {"reloadfs Tutorial"}
    };
    return SendRconCommand(g_Commands[random(sizeof(g_Commands))]);
}



Re: To optimize our GM and our Server improves. - Ada32 - 23.12.2013

what in the heck's a curl??


Re: To optimize our GM and our Server improves. - Bakr - 23.12.2013

Nice, just a few things I'd like to point out:

1) For your example under "cells", you only need 61. 30 + 24 + 6 + 1. Though I don't think 9 cells has ever killed anyone.

2) Global variables (static or new) are only created once. The use of "static" for global variables is to restrict them to the file they were created in (useful for encapsulating libraries). Note that it can also be used the same for functions
pawn Code:
// function only accessible in the file it was created in
static stock HiddenFunc() {}
Using "static" for local variables creates them once. They technically ARE global variables, they just don't clutter up the global namespace.

To summarize: Use "static" for functions/global variables to restrict them to the file they were created in. Use "static" for local variables that do not need to be constantly re-created (this is useful as they retain their previous value until overwritten).

3) For using the switch keyword, it should be noted the cases need be constant values.

Just as a note, "curls" are known as "loops".


Respuesta: Re: To optimize our GM and our Server improves. - Swedky - 23.12.2013

nGen.SoNNy: Thank you friend, I estimate very much the support.


Emmet_: Excellent example, he had not thought it to use that way.


Quote:
Originally Posted by Bakr
View Post
Nice, just a few things I'd like to point out:


2) Global variables (static or new) are only created once. The use of "static" for global variables is to restrict them to the file they were created in (useful for encapsulating libraries). Note that it can also be used the same for functions
pawn Code:
// function only accessible in the file it was created in
static stock HiddenFunc() {}
Using "static" for local variables creates them once. They technically ARE global variables, they just don't clutter up the global namespace.

To summarize: Use "static" for functions/global variables to restrict them to the file they were created in. Use "static" for local variables that do not need to be constantly re-created (this is useful as they retain their previous value until overwritten).

3) For using the switch keyword, it should be noted the cases need be constant values.
Good information, it is good to know.

Quote:
Originally Posted by Bakr
View Post
1) For your example under "cells", you only need 61. 30 + 24 + 6 + 1. Though I don't think 9 cells has ever killed anyone.
It had not noticed it. I forgot to use the quantities of cells in English, since they were for the exact quantity with the message in Spanish.


Quote:
Originally Posted by Bakr
View Post
Just as a note, "curls" are known as "loops".
This way me this word appeared on having translated it. (In Spanish it is: bucles).


Re: To optimize our GM and our Server improves. - Konstantinos - 23.12.2013

It's a good tutorial.

Although, I'd like to point out that using:

pawn Code:
stock GetName(playerid) // Code used in previous examples in this tutorial.
{
    new Nick[MAX_PLAYER_NAME];
    GetPlayerName(playerid, Nick, sizeof(Nick));
    return Nick; // He returns a chain of text.
}
is a really bad idea. Let's say in a command you want to use a player's name 5 times so you're going to call GetPlayerName 5 times when it could be done only 1 (by getting the name right there). However, the best way (atleast for me) is to get the name only once on connect and store it to a global array (either packed string or not). Then you can use to pass the name as an argument wherever you want.

You also said about foreach instead of for loops for the players and foreach is really great! foreach should be used for the vehicles as well and it's very optimized. Instead of looping 2000 times for vehicles, you can add vehicleid to an Iterator and loop through the valid vehicleids. So let's say you have 2 vehicles in the server, it will only loop 2 times instead of 2000 and imagine if it had to loop many times through the vehicles.

PS: It's y_commands and not Y_CMD.


Respuesta: Re: To optimize our GM and our Server improves. - Swedky - 23.12.2013

Quote:
Originally Posted by Konstantinos
View Post
It's a good tutorial.

Although, I'd like to point out that using:

pawn Code:
stock GetName(playerid) // Code used in previous examples in this tutorial.
{
    new Nick[MAX_PLAYER_NAME];
    GetPlayerName(playerid, Nick, sizeof(Nick));
    return Nick; // He returns a chain of text.
}
is a really bad idea. Let's say in a command you want to use a player's name 5 times so you're going to call GetPlayerName 5 times when it could be done only 1 (by getting the name right there). However, the best way (atleast for me) is to get the name only once on connect and store it to a global array (either packed string or not). Then you can use to pass the name as an argument wherever you want.

You also said about foreach instead of for loops for the players and foreach is really great! foreach should be used for the vehicles as well and it's very optimized. Instead of looping 2000 times for vehicles, you can add vehicleid to an Iterator and loop through the valid vehicleids. So let's say you have 2 vehicles in the server, it will only loop 2 times instead of 2000 and imagine if it had to loop many times through the vehicles.

PS: It's y_commands and not Y_CMD.
You refer to that something like that:


pawn Code:
CMD:mynick(playerid, params[])
{
    new string[126];
    format(string, sizeof(string), "%s %s %s %s %s.", GetName(playerid), GetName(playerid), GetName(playerid), GetName(playerid), GetName(playerid));
    SendClientMessage(playerid, -1, string);
    return 1;
}

Would it be bad?.

No understood eh very well :S.



Re: To optimize our GM and our Server improves. - xVIP3Rx - 23.12.2013

Great tutorial
But I have a question..

You say that we should use static globally because its created once until the server is closed ? But Isn't that the same as "New" ?
Also as I knew from the wiki Ithat I can use it locally if I don't want it to forget it's value, like declaring a global variable locally..
And globally just If I will only use it in the file I declared it in.. Is there any other reason/something I should use it for ?

Quote:
Originally Posted by EnzoMetlc
View Post
You refer to that something like that:


pawn Code:
CMD:mynick(playerid, params[])
{
    new string[126];
    format(string, sizeof(string), "%s %s %s %s %s.", GetName(playerid), GetName(playerid), GetName(playerid), GetName(playerid), GetName(playerid));
    SendClientMessage(playerid, -1, string);
    return 1;
}

Would it be bad?.

No understood eh very well :S.
Yes, It'll call "GetName" 5 times when you can get the name once.

That actually noticed me, I was using it in a command like 10 times.. Thanks Konstantinos


Re: To optimize our GM and our Server improves. - Patrick - 23.12.2013

@EnzoMetlc - If you do not understand what Konstantinos said, I'll elaborate it, so he means, you could use one (1) variable to get the Players name using OnPlayerConnect then you can just use the variable where ever you want, without using the function GetPlayerName five(5) times or more than that, the example code will be shown below.

pawn Code:
#include <a_samp>
#include <YSI\y_va>

main()
{

}

new
    PlayerName[MAX_PLAYERS][MAX_PLAYER_NAME];

public OnPlayerConnect(playerid)
{
    GetPlayerName(playerid, PlayerName[playerid], 24);
    return true;
}

public OnPlayerSpawn(playerid)
{
    return SendClientMessageToAllEx(-1, "[DEBUG]: GetPlayerName - %s[%i]", PlayerName[playerid], playerid);
}

stock SendClientMessageToAllEx(colour, const fmat[], va_args<>)
{
    new
        str[145];
    va_format(str, sizeof (str), fmat, va_start<2>);
    return SendClientMessageToAll(colour, str);
}
Result
Code:
[12:55:20] [DEBUG]: GetPlayerName - Matthew_Sixx[0]
Hope you understand it

PS: Matthew_Sixx is my roleplay name :P


Re: To optimize our GM and our Server improves. - Konstantinos - 23.12.2013

Not exactly in one line. In some cases you need to send more than 2 messages with the name to the player who typed the command or others (to all).

A better example would be:
pawn Code:
CMD:kick(playerid, params[])
{
    new
        id,
        reason[100];

    if (sscanf(params, "rs[100]", id, reason)) return // syntax..

    // ...

    new
        string[128],
        ip[16];

    GetPlayerIp(id, ip, 16);

    format(string, sizeof (string), "You have been kicked by %s. Reason: %s", GetName(playerid), reason);
    SendClientMessage(id, -1, string);

    format(string, sizeof (string), "You kicked %s. Reason: %s", GetName(id), reason);
    SendClientMessage(playerid, -1, string);

    format(string, sizeof (string), "%s kicked %s. Reason: %s", GetName(playerid), GetName(id), reason);
    SendClientMessageToAll(id, -1, string);

    format(string, sizeof (string), "%s kicked %s [IP: %s]. Reason: %s", GetName(playerid), GetName(id), ip, reason);
    SendMessageToAdmins(-1, string);
   
    // kick player..
    return 1;
}



Re: To optimize our GM and our Server improves. - Bakr - 23.12.2013

Quote:
Originally Posted by xVIP3Rx
View Post
You say that we should use static globally because its created once until the server is closed ? But Isn't that the same as "New" ?
For global variables, yes.

Quote:
Originally Posted by xVIP3Rx
View Post
Also as I knew from the wiki Ithat I can use it locally if I don't want it to forget it's value, like declaring a global variable locally..
And globally just If I will only use it in the file I declared it in.. Is there any other reason/something I should use it for ?
Not really, but that should be enough. Hiding implementation details and data from the end user is good programming practice. Look up encapsulation.


Re: To optimize our GM and our Server improves. - Ada32 - 23.12.2013

oh and these aren't optimizations btw, Slice's tips & tricks and Y_Less' code optimization topics are optimization threads, this is just..well, idk..


Re: To optimize our GM and our Server improves. - xVIP3Rx - 23.12.2013

Quote:
Originally Posted by Bakr
View Post
For global variables, yes.


Not really, but that should be enough. Hiding implementation details and data from the end user is good programming practice. Look up encapsulation.
So you say that the end user is able to get the global "New"s in samp ?
Or you mean for other programs ?


Re: To optimize our GM and our Server improves. - Riddick94 - 23.12.2013

Delete please.


Re: To optimize our GM and our Server improves. - Konstantinos - 23.12.2013

No need to use strmid or any temporary string to store the name. Just:
pawn Code:
GetPlayerName(playerid, PlayerName[playerid], MAX_PLAYER_NAME);



Re: To optimize our GM and our Server improves. - Riddick94 - 23.12.2013

Quote:
Originally Posted by Konstantinos
View Post
No need to use strmid or any temporary string to store the name. Just:
pawn Code:
GetPlayerName(playerid, PlayerName[playerid], MAX_PLAYER_NAME);
Yeah, noticed that after. Nevermind my message.


Re: To optimize our GM and our Server improves. - newbienoob - 23.12.2013

I've learned a lot in this tutorial (not just from the OP but from the posts). Just 1 question. Is it okay to do something like this?

pawn Code:
public OnGameModeInit()
{
    Timer = SetTimer("OneSecTimer", 1000, true);
    return 1;
}

CMD:healme(playerid)
{
    if(IsHealed[playerid] == 1)
    {
        SendClientMessage(playerid, -1, "Please wait 1 minute before using this command again!");
        HealTimer[playerid] == 60; // 60 seconds
    }
    else
    {
        SetPlayerHealth(playerid, 100);
        IsHealed[playerid] = 1;
    }
    return 1;
}


CMD:givearmour(playerid)
{
    if(Armoured[playerid] == 1)
    {
        SendClientMessage(playerid, -1, "Please wait 1 minute before using this command again!");
        ArmourTimer[playerid] = 60;
    }
    else
    {
        SetPlayerArmour(playerid, 100);
        Armoured[playerid] = 1;
    }
    return 1;
}


forward OneSecTimer();
public OneSecTimer()
{
    for(new i = 0; i < MAX_PLAYERS; i++) //foreach
    {
        if(IsPlayerConnected(i))
        {
            if(IsHealed[i] == 1)
            {
                Healtimer[i]--;
                if(HealTimer[i] == 0)
                {
                    IsHealed[i] = 0;
                    SendClientMessage(i, -1, "You can now use /healme command again");
                }
            }
           
            if(Armoured[i] == 1)
            {
                Healtimer[i]--;
                if(HealTimer[i] == 0)
                {
                    IsHealed[i] = 0;
                    SendClientMessage(i, -1, "You can now use /healme command again");
                }
            }

        }
    }
    return 1;
}
Instead of

pawn Code:
CMD:healme(playerid)
{
    if(IsHealed[playerid] == 1)
    {
        SendClientMessage(playerid, -1, "Please wait 1 minutes before using this command again!");
        HealTimer = SetTimerEx("Heal", 60 * 1000, false, "i", playerid);
    }
    else
    {
        SetPlayerHealth(playerid, 100);
        IsHealed[playerid] = 1;
    }
    return 1;
}

forward Heal(playerid);
public Heal(playerid)
{
    IsHealed[playerid] = 0;
    SendClientMessage(playerid, -1, "You can now use /healme command again");
    KillTimer(HealTimer);
    return 1;
}

CMD:givearmour(playerid)
{
    if(Armoured[playerid] == 1)
    {
        SendClientMessage(playerid, -1, "Please wait 1 minutes before using this command again!");
        ArmourTimer = SetTimerEx("Armour", 60 * 1000, false, "i", playerid);
    }
    else
    {
        SetPlayerArmour(playerid, 100);
        Armoured[playerid] = 1;
    }
    return 1;
}

forward Armour(playerid);
public Armour(playerid)
{
    Armoured[playerid] = 0;
    SendClientMessage(playerid, -1, "You can now use /givearmour command again");
    KillTimer(ArmourTimer);
    return 1;
}
Using 1 timer instead of many.


Re: To optimize our GM and our Server improves. - Patrick - 23.12.2013

newbienoob - There's a method that is much better than that , I will share it to you, you could use the function gettime, to create a timer for a command, I used to use the first method, creating a second timer and loop it, but when the callback gets larger, You will experience lag, but the code below is just amazing

pawn Code:
CMD:healme(playerid, params[])
{
    static
        HealTimer[ MAX_PLAYERS ] = { 0, ... }
    ;

    if( (gettime( ) - HealTimer[playerid] ) < 60)
        return SendClientMessage(playerid, -1, "Please wait 1 minute before using this command again!");

    HealTimer[playerid] = gettime();

    SetPlayerHealth(playerid, 100);
    IsHealed[playerid] = 1;
    return 1;
}



Re: To optimize our GM and our Server improves. - newbienoob - 23.12.2013

The code I posted was just an example. I could use random messages, unmuting/unjailing, updating hour's playing... etc. Something like this
pawn Code:
public OneSecTimer()
{
foreach(...)
{
    if(Player[i][IsLogged] == 1)
    {
        Player[i][updatept]++;
        if(Player[i][updatept] == 3600) //1 hour
        {
            pInfo[i][PT]++;
            Player[i][updatept] = 0;
            SendClientMessage(i,-1,""hinfo" You have received 1 PlayTime score.");
        }
//....

        if(pInfo[i][Muted] == 1 && pInfo[i][MuteTime] > 0)
        {
            pInfo[i][MuteTime]--;
            if(pInfo[i][MuteTime] == 0)
            {
                pInfo[i][Muted] = 0;
                pInfo[i][MuteTime] = 0;
                SendClientMessage(i,-1,""hinfo" You have been unmuted!");
            }
        }
        if(pInfo[i][Jailed] == 1 && pInfo[i][JailTime] > 0 && Player[i][Spawned] == 1)
        {
            pInfo[i][JailTime]--;
            if(pInfo[i][JailTime] == 0)
            {
                pInfo[i][Jailed] = 0;
                pInfo[i][JailTime] = 0;
                SetPlayerHealth(i,0);
                SendClientMessage(i,-1,""hinfo" You have been unjailed!");
            }
        }

//random messages
    Player[i][MSGTimer]++;
    if(Player[i][MSGTimer] == 7)
    {
        PlayerTextDrawSetString(i, RandTD[i], RandMSG[rand]); //updating textdraw/sending messages(sendclientmessage) every 7 seconds
        Player[i][MSGTimer] = 0;
    }

//from my old script
Is it a good way?