[Tutorial] Creating cleaner and more read-able code
#1

I see alot of people having un-clean and messy codes, therefore im gonna create this tutorial to help those people.

Description
In this tutorial you will be able to learn how to create more tidy, clean, and read-able code in your script.

Part 1 - Indentation
You probably know how indentation works, but i decided to include this anyways for any people who does not.
Some people bypass the warnings caused by bad indentation by using a piece of code.
Even though that will remove the warnings, your code will be hard to read if you have everything like this:
pawn Code:
CMD:toggle(playerid, params[])
{
if(sscanf(params, "s[14]", params))
{
SendClientMessage(playerid, -1, "/toggle [option]");
SendClientMessage(playerid, -1, "Options: PM");
}
if(!strcmp(params, "pm", true))
{
switch(pm[playerid])
{
case 0:
{
pm[playerid] = 1;
SendClientMessage(playerid, -1, "You have toggled PM's off.");
}
case 1:
{
pm[playerid] = 0;
SendClientMessage(playerid, -1, "You have toggled PM's on.");
}
}
}
return 1;
}
More people using this method also looses track of where to put brackets, as you cannot compare it anymore.
Normally on the spacings you can see how all the brackets affects your code, thereby you are able to see if you are missing one somewhere.
pawn Code:
CMD:toggle(playerid, params[])
{
    if(sscanf(params, "s[14]", params))
    {
        SendClientMessage(playerid, -1, "/toggle [option]");
        SendClientMessage(playerid, -1, "Options: PM");
    }
    if(!strcmp(params, "pm", true))
    {
        switch(pm[playerid])
        {
            case 0:
            {
                pm[playerid] = 1;
                SendClientMessage(playerid, -1, "You have toggled PM's off.");
            }
            case 1:
            {
                pm[playerid] = 0;
                SendClientMessage(playerid, -1, "You have toggled PM's on.");
            }
        }
    }
    return 1;
}
After each opening bracket, you should go 4 spaces forward (equivalent to pressing TAB once).
And after each closing bracket, you should go 4 spaces backwards.

If you have alot of code that needs to be moved forwards or backwards, and you do not want to "TAB it out" on each line, you can simply mark the whole code,
and press TAB to move it forwards, or press TAB while holding SHIFT to move it backwards.

Part 2 - Use of brackets - 1/2
Using brackets can be quite straight-forward, but even though you might be using them right, you might not need them at all.
Here's an example of over-used brackets:
pawn Code:
CMD:pm(playerid, params[])
{
    new targetid, message[128];
    if(sscanf(params, "us", targetid, message))
    {
        SendClientMessage(playerid, -1, "/pm [playerid] [message]");
        return 1;
    }
    if(pm[playerid] == 0)
    {
        SendClientMessage(playerid, -1, "Your PM's are turned off.");
    }
    else if(pm[targetid] == 1)
    {
        SendClientMessage(playerid, -1, "Your PM's are turned on.");
    }
    else
    {
        SendClientMessage(targetid, -1, "PM Received.");
        SendClientMessage(playerid, -1, "PM Sent");
    }
    return 1;
}
As you can see, that will work fine, but there's no need for all those brackets.
So let's try to clean this code up a little.
Let's take a look at the first piece of the code:
pawn Code:
if(sscanf(params, "us", targetid, message))
{
    SendClientMessage(playerid, -1, "/pm [playerid] [message]");
    return 1;
}
The first mistake, is that returning 1, is the same as returning SendClientMessage.
pawn Code:
if(sscanf(params, "us", targetid, message))
{
    return SendClientMessage(playerid, -1, "/pm [playerid] [message]");
}
Therefore that code will work just like the other.
But it can be improved further, as using brackets to enclose 1 function is not necessary.
Therefore you can do this aswell:
pawn Code:
if(sscanf(params, "us", targetid, message)) return SendClientMessage(playerid, -1, "/pm [playerid] [message]");
And it would work just like the first example.
As you can see, it only takes up 1 line, where-as the first example took up 5 lines, and it works just as well as the first one too.

So now we can do the same with the rest of the command aswell, as most of it is brackets enclosing single functions.
pawn Code:
CMD:pm(playerid, params[])
{
    new targetid, message[128];
    if(sscanf(params, "us", targetid, message)) return SendClientMessage(playerid, -1, "/pm [playerid] [message]");
    if(pm[playerid] == 0) SendClientMessage(playerid, -1, "Your PM's are turned off.");
    else if(pm[targetid] == 1) SendClientMessage(playerid, -1, "Your PM's are turned on.");
    else
    {
        SendClientMessage(targetid, -1, "PM Received.");
        SendClientMessage(playerid, -1, "PM Sent");
    }
    return 1;
}
As you can see, this is alot shorter than the first example.
But with this code, we need to use brackets around it, making the "else" trigger multiple functions.
pawn Code:
else
{
    SendClientMessage(targetid, -1, "PM Received.");
    SendClientMessage(playerid, -1, "PM Sent");
}
That will make the else work fine, but doing this:
pawn Code:
else SendClientMessage(targetid, -1, "PM Received."); SendClientMessage(playerid, -1, "PM Sent");
Will not work correctly, as the editor sees it like this:
pawn Code:
else SendClientMessage(targetid, -1, "PM Received.");
SendClientMessage(playerid, -1, "PM Sent");
Therefore having only the "else" trigger one of the SendClientMessages, while the other get's triggered no matter what.

Part 3 - Use of brackets - 2/2

Brackets can be used in different ways, one way you can use them is to do something like this:
pawn Code:
CMD:pm(playerid, params[])
{
    new targetid, message[128];
    if(sscanf(params, "us", targetid, message)) return SendClientMessage(playerid, -1, "/pm [playerid] [message]");
    if(pm[playerid] == 0) SendClientMessage(playerid, -1, "Your PM's are turned off.");
    else if(pm[targetid] == 1) SendClientMessage(playerid, -1, "Your PM's are turned on.");
    else
    {
        SendClientMessage(targetid, -1, "PM Received.");
        SendClientMessage(playerid, -1, "PM Sent");
    }
    return 1;
}
As we have done in the rest of the tutorial, but you can use them in a different way.
You can place them on the same line as you use functions, here's an example:
pawn Code:
[pawn]CMD:pm(playerid, params[]) {
    new targetid, message[128];
    if(sscanf(params, "us", targetid, message)) return SendClientMessage(playerid, -1, "/pm [playerid] [message]");
    if(pm[playerid] == 0) SendClientMessage(playerid, -1, "Your PM's are turned off.");
    else if(pm[targetid] == 1) SendClientMessage(playerid, -1, "Your PM's are turned on.");
    else {
        SendClientMessage(targetid, -1, "PM Received.");
        SendClientMessage(playerid, -1, "PM Sent"); }
    return 1; }
[/pawn]
As you can see, this shortens your code alot when using advanced codes, but it does make your code harder to read, and might also make you miss brackets as they're not that standing on their own lines for you to see anymore.

Part 3 - Operators - "==, >, !, &&, ||"
Short explanations:
(something) == (something else) - Something equal to something else.

(something) > (something else) - Something higher than something else.

(something) < (something else) - Something lower than something else.

(something) && (something else) - Something and something else.

(something) || (something else) - Something or something else.

"!" is a bit special, it cannot be used as above, but can be used like this:

(something) != (something else) - Something not equal to something else.

!(variable/function) - Checks if a variable is set to 0, or a function returns 0.

Some people create long codes to do the same as a simple ">" would do, so remember to have these in thoughts.
Especially the "!", as in functions like strcmp it can help to shorten codes.
Without "!":
pawn Code:
if(strcmp(params, "pm", true) == 0)
With "!":
pawn Code:
if(!strcmp(params, "pm", true))
Part 4 - Custom functions
There are functions that a_samp gives you by default, like SetPlayerHealth and such, but if you didn't know, you can create your own.
By this way you can massively tone down your script, especially if you use alot of the same code different places, you can just put it into a simple function and use that.
Here's an example of a custom GivePlayerMoney function:
pawn Code:
SetPlayerMoney(playerid, amount)
{
    ResetPlayerMoney(playerid);
    return GivePlayerMoney(playerid, amount);
}
Then if we want to set a player's money to 100, simply do this:
pawn Code:
SetPlayerMoney(playerid, 100);
This may not be the most advanced code, but you get the idea.

Part 5 - Defines
Using defines is a very good way to make your code better, and even more read-able.
A define is just a way to rename something to something else.
Etc. using red color in SendClientMessage, instead of doing this:
pawn Code:
CMD:redmessage(playerid, params[]) return SendClientMessage(playerid, 0xFF0000AA, "This message is red");
Again, you do not need brackets in a case like this.
But instead of using many different 0x?? ?? ?? which you cannot organize very well, you can just use this:
pawn Code:
#define COLOR_RED 0xFF0000AA // Add at the top of your script

CMD:redmessage(playerid, params[]) return SendClientMessage(playerid, COLOR_RED, "This message is red");
Thereby you won't loose track if you use red colors that are slightly different, and if you need to change the red color, you can just change the define.

That's all i have for now, i know alot of people who want to clean and tidy up their code, so i hope it will help you.
I will update this tutorial if i find any mistakes or other improvements.
Reply
#2

useful tutorial +rep for you
rep me back if you want thnx
best regards
Reply
#3

Awesome tutorial!
Reply
#4

Quote:
Originally Posted by CalvinC
View Post
And just change it into this:
pawn Code:
else SendClientMessage(targetid, -1, "PM Received."); SendClientMessage(playerid, -1, "PM Sent");
If you don't use brackets only the first next line is taken (a line ends with the breaking operator ";")
Therefor only the first SendClientMessage is included in the else

To do this right you should either use brackets for more than one function or you use a normal comma ","
pawn Code:
else SendClientMessage(targetid, -1, "PM Received."), SendClientMessage(playerid, -1, "PM Sent");
Reply
#5

Quote:
Originally Posted by Nero_3D
View Post
If you don't use brackets only the first next line is taken (a line ends with the breaking operator ";")
Therefor only the first SendClientMessage is included in the else

To do this right you should either use brackets for more than one function or you use a normal comma ","
pawn Code:
else SendClientMessage(targetid, -1, "PM Received."), SendClientMessage(playerid, -1, "PM Sent");
Oh sorry, i actually thought it only worked with 1 function, unless you use brackets, but then i experimented a bit with it without it returning errors, so i changed it.
But i changed it back now, thanks.
Reply
#6

My scripting looks a lot like this, although I was self-taught, so yeah. Cool tut. My biggest pet peeve is indentation when trying to read code.
Reply
#7

Really good for
Nice Work CalvinC REP +
Reply
#8

Since when amounts (Game money) are floats?
Reply
#9

Quote:
Originally Posted by biker122
View Post
Since when amounts (Game money) are floats?
Ah thanks, corrected that too, i haven't tested any of the code, as the tutorial doesn't focus on it, so i don't guarantee the functions to work.
Reply
#10

Correct me if I'm wrong.

Your PM variable can, logically, have two values: 0 (for disabled) and 1 (for enabled). Hypothetically speaking, I can then assign it to any value higher than 1 or lower than 0 at any given moment. I try to define variables like this as a Boolean when possible since a Boolean can have two values: true or false.
Reply
#11

Personally, I prefer to check everything before allowing the command to process, it saves all of that code being processed before realising 'oh wait, nvm I can't do that, get rekt kid mlg 360 noscopes and I just used data.

Simply, perform your checks at the top like this:

Code:
    if(AdminLevel[playerid] < 3) return SendClientMessage(playerid, COLOUR_GREY, "You are not authorized to use this command.");
    if(sscanf(params, "us[128]", giveplayerid, reason)) return SendClientMessage(playerid, COLOUR_GREY, "Usage: /kick [player id] [reason]");
	if(!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid, COLOUR_GREY, "That player is not connected.");
My Kick command looks tidier IMO.

Code:
CMD:kick(playerid, params[])
{
    if(LoggedIn[playerid] == 0) return SendClientMessage(playerid, COLOUR_GREY, "You must be logged in to use this command.");
    
    new reason[128], giveplayerid;
    if(AdminLevel[playerid] < 3) return SendClientMessage(playerid, COLOUR_GREY, "You are not authorized to use this command.");
    if(sscanf(params, "us[128]", giveplayerid, reason)) return SendClientMessage(playerid, COLOUR_GREY, "Usage: /kick [player id] [reason]");
    if(!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid, COLOUR_GREY, "That player is not connected.");
	
    format(reason, sizeof(reason), "Admin %s Kicked %s [Reason: %s ]", GetNameEx(playerid), GetNameEx(giveplayerid), reason);
    SendClientMessageToAll(COLOUR_REALRED, reason);
    SaveWeapons(giveplayerid);
    SavePlayerData(giveplayerid);
    SetTimerEx("KickPlayer",1000,false,"i",giveplayerid);// Kicks player in 500ms
    return 1;
}
Reply
#12

Proper form of Control Structures.
You have added the general ones (>, !, ==) but what about Triadic opreators (? & :)!
These would help to shorten the code. Just for an example:
Code:
new bool:x[2];
main()
{
       x[0] = true;
       if(x[0])
       {
                x[1] = true;
        }
        else 
        {
                x[1] = false;
         }
}
Now that code can also be written as:
Code:
new bool:x[2];

main()
{
        x[1] = (x[0] == true) ? (true) : (false);
}
Though you can also do that^^ with more vars.

Implementing in a pm script:
Code:
CMD:togglepm(playerid, params[])
{
        pm[playerid] = ( pm[playerid] == true ) ? ( false ) : ( true );
        return 1;
}
Reply
#13

You know shorter code is not always more readable.. Dont worry about performance until it becomes an issue.
Also the return statements in commands have a function, they let samp know if the command exists or not. You can't guarantee sa-mp Will always return 1 from sendclientmessage. Just use return 1; . That's more readable. Don't put multiple statements on one line. Don't make very long functie calls/if's - split the arguments/expressions onto multiple lines. Indentation is important but it's not 4 spaces perse, advanced editors can set how many spaces a tab is. Use variables instead of defines where possible to avoid nasty surprises.
Reply
#14

I made this tutorial for a friend that wanted to tidy his code a lot, but didn't know how to.
But i see im learning a lot from comments here that i didn't know about, like return SCM, i've never really thought about that, or understood triadic operators.
So thanks for all these tips guys, guess i still have alot to learn about this aswell.
Reply
#15

Quote:
Originally Posted by Gammix
View Post
Proper form of Control Structures.
You have added the general ones (>, !, ==) but what about Triadic opreators (? & !
These would help to shorten the code. Just for an example:
Code:
new bool:x[2];
main()
{
       x[0] = true;
       if(x[0])
       {
                x[1] = true;
        }
        else 
        {
                x[1] = false;
         }
}
Now that code can also be written as:
Code:
new bool:x[2];

main()
{
        x[1] = (x[0] == true) ? (true) : (false);
}
Though you can also do that^^ with more vars.

Implementing in a pm script:
Code:
CMD:togglepm(playerid, params[])
{
        pm[playerid] = ( pm[playerid] == true ) ? ( false ) : ( true );
        return 1;
}
If you just want to swap a boolean value you might as well just do.
Code:
var = !var;
I don't really like the way people put all their "pre-command checks" on a single line. In the case of those "pre-command checks" I put the check, then the return indented on the line below follwed by a blank line. I always have an extreme urge to line everything up. Especially in case of coordinates. It will usually look like this (screenshot to preserve identation): http://puu.sh/guaXZ/59b9cad49e.png
Reply
#16

Quote:
Originally Posted by Vince
View Post
I always have an extreme urge to line everything up. Especially in case of coordinates. It will usually look like this (screenshot to preserve identation): http://puu.sh/guaXZ/59b9cad49e.png
Our urges should get along together and have babies! xD They're the sameeee

But ye this looks much better than this.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)