[Tutorial] Frequently Asked Questions Thread.
#1

This is a FAQ which I will be adding most asked questions, I added some which ****** have written them a while ago and adding more soon.
Suggestions are welcome in replies about the popular questions.

____________________________

How do I convert strcmp to y_commands?
The simplest way to explain this is with an example:

pawn Код:
public OnPlayerCommandText(playerid, cmdtext[])
{
    new
        index,
        cmd[32];
    cmd = strtok(cmdtext, index);

    if (strcmp(cmd, "/heal", true) == 0)
    {
        new
            tmp[32];
        tmp = strtok(cmdtext, index);
        if (!strlen(tmp)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /heal <ID>");
        if (!IsPlayerConnected(strval(tmp))) return SendClientMessage(playerid, 0xFF0000AA, "Player not found");
        SetPlayerHealth(strval(tmp), 100.0);
        SendClientMessage(strval(tmp), 0x00FF00AA, "You have been healed");
        SendClientMessage(playerid, 0x00FF00AA, "Player healed");
        return 1;
    }
    // OTHER COMMANDS.
    return 0;
}
Here we have one command, "/heal" that we want to convert. y_commands doesn't use "OnPlayerCommandText",
so we can delete all of that outside our command:

pawn Код:
if (strcmp(cmd, "/heal", true) == 0)
{
    new
        tmp[32];
    tmp = strtok(cmdtext, index);
    // COMMAND BODY.
    return 1;
}
// OTHER COMMANDS.
The first line is now the part that compares what the user typed (at least the first part of what
they typed) to one of the possible commands. We need to change that to y_commands syntax:

pawn Код:
if (strcmp(cmd, "/heal", true) == 0)
The command is called "heal", and all y_commands commands start with "CMD:" and take two parameters after
the command name. This gives us:

pawn Код:
CMD:heal(playerid, params[])
This now gives us:

pawn Код:
CMD:heal(playerid, params[])
{
    new tmp[32];
    tmp = strtok(cmdtext, index);
    // COMMAND BODY.
    return 1;
}
// OTHER COMMANDS.
We are almost there, but this won't yet compile because of lines like this:

pawn Код:
tmp = strtok(cmdtext, index);
Really, you should be using "sscanf" to handle parameters, but for simplicity we will convert this
as well. The problem is that we have deleted the code in which "index" is declared and no longer
have "cmdtext" either. Solve this by declaring a new variable called "index" and changing "cmdtext"
to "params":

pawn Код:
CMD:heal(playerid, params[])
{
    new tmp[32];
    new index; // New variable.
    tmp = strtok(params, index); // Changed variable.
    // COMMAND BODY.
    return 1;
}
// OTHER COMMANDS.
NOTE: If you are going to convert from strcmp to ZCMD, I highly recommend changing to sscanf too.

Is there a way to automate this?
Yes, if your editor supports "Regular Expressions" (aka "RegEx"). Both Sublime Text and Notepad++
support this tool, which looks for text patterns and replaces those patterns with other patterns of
text. This isn't a tutorial on RegEx itself, nor on those editors (both of which are well
documented elsewhere) but you can use this search/replace system:

Search Pattern:

Код:
if[ (!]*strcmp[ (]*cmd.*/(.*)".*
Replace Pattern:

Код:
CMD:\1\(playerid, params[]\)
That will replace any of these styles of command headers:

pawn Код:
if (strcmp(cmd, "/heal", true) == 0)
if (!strcmp (cmd, "/heal", true) == 0)
if ( strcmp( cmdtext, "/heal", true, 5 ) == 0 )
if ( !strcmp(cmdtext, "/heal", true, 5) )
Also more spacious options. It will NOT replace:

pawn Код:
if (strcmp(cmd[1], "heal", true) == 0)
The "/" is explicitly searched for.

You still need to delete the remainder of "OnPlayerCommandText" yourself, and add in any newly
required "index" variables yourself. Changing "cmdtext" to "params", though, is a simple search and
replace job.

How do I convert DCMD to y_commands?
This is much simpler than converting "strcmp" to "y_commands" because all the commands are already in
functions. The first step is simple - delete "OnPlayerCommandText", all of it. No demo necessary,
just highlight the function and press "delete".

Now we need to go from this:

pawn Код:
dcmd_heal(playerid, params[])
To this:

pawn Код:
CMD:heal(playerid, params[])
This is not hard at all. You can either manually replace "dcmd_" with "CMD:", or you can do a
global search and replace to have the computer do it for you. Just be careful that there aren't any
instances of that text that you don't want to be replaced.

How do I convert y_commands to ZCMD?
"ZCMD" and "y_commands" both use the same syntax for declaring commands, so there is no need to
convert at all. This is a command in "y_commands":

pawn Код:
CMD:heal(playerid, params[])
This is a command in "ZCMD":

pawn Код:
CMD:heal(playerid, params[])
"y_commands" can have a third "help" parameter, but this is OPTIONAL. If you want to use this you
need to change "CMD:" to "YCMD:" and add in the third parameter:

pawn Код:
YCMD:heal(playerid, params[], help)
You should also probably USE the "help" parameter:

pawn Код:
YCMD:heal(playerid, params[], help)
{
    if (help)
    {
        return SendClientMessage(playerid, 0xFF0000AA, "Heals a player.  Usage : /heal <ID>");
    }
    else
    {
        // Do the actual command.
        return 1;
    }
}
The body of the "else" branch is just all of the old code. The body of the "then" branch is
instructions on what the command does and how to use it, i.e. "help" on the command.

How do I convert strtok to sscanf?
Most people think of "sscanf" as part of "ZCMD" - it isn't. There is no reason why you can't use
"strcmp" with "sscanf", or "strcat" with "ZCMD" (as shown in some other questions). The reason that
"sscanf" is often grouped with "ZCMD" is that they are both good techniques and produce better
commands than any using "strcmp" or "strtok". But because they are separate, converting the parts
is done separately as well.

Let us start with a command. This is a "/givecash" command, but includes an optional reason of WHY
you are sending that player money (to demonstrate a range of parameter types). It is written in
ZCMD, but could have just been converted to that from "strcmp":

pawn Код:
CMD:givecash(playerid, params[])
{
    new
        tmp[32],
        index;
    // Get the player to give money to.
    tmp = strtok(params, index);
    if (!strlen(tmp)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /givecash <ID> <Amount> [Optional reason]");
    new
        giveplayerid = strval(tmp);
    if (!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid, 0xFF0000AA, "Player not found.");
    // Get the amount of money to give.
    tmp = strtok(params, index);
    if (!strlen(tmp)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /givecash <ID> <Amount> [Optional reason]");
    new
        amount = strval(tmp);
    if (amount < 0) return SendClientMessage(playerid, 0xFF0000AA, "You must enter a positive amount of money.");
    else if (amount >= GetPlayerMoney(playerid)) return SendClientMessage(playerid, 0xFF0000AA, "You do not have that much money to give.");
    // Get the optional message.
    new
        reason[128];
    strcpy(reason, params[index]);
    // Do the transfer.
    GivePlayerMoney(playerid, -amount);
    GivePlayerMoney(giveplayerid, amount);
    // Send messages.
    new
        message[128];
    if (strlen(reason))
    {
        format(message, sizeof (message), "You have sent $%d to %d for reason: %s", amount, giveplayerid, reason);
        SendClientMessage(playerid, 0xFF0000AA, message);
        format(message, sizeof (message), "%d has sent you $%d for reason: %s", playerid, amount, reason);
        SendClientMessage(giveplayerid, 0xFF0000AA, message);
    }
    else
    {
        format(message, sizeof (message), "You have sent $%d to %d for no reason", amount, giveplayerid);
        SendClientMessage(playerid, 0xFF0000AA, message);
        format(message, sizeof (message), "%d has sent you $%d for no reason", playerid, amount);
        SendClientMessage(giveplayerid, 0xFF0000AA, message);
    }
    return 1;
}
Apologies for the length of this command, but that is because of the use of "strtok". There is no
automatic way to convert to "sscanf", and the best way to learn what to do is by reading the
documentation, but we will try and explain this conversion process.

There are three parameters in the above command, each represented by a different letter in "sscanf"
- a target player (playerid, "u"), an amount (integer, "i"), and a reason (optional string,
"S[length]"). "sscanf" takes a data source, a description of how the data in that source is
formatted, and variables - one for each piece of data to be stored in:

pawn Код:
new
    giveplayerid,
    amount,
    reason[128];
sscanf(params, "uiS[128]", giveplayerid, amount, reason);
It also returns 0 (zero) when the data is correctly extracted, and NOT 0 when the data isn't
correctly extracted (i.e. when the wrong data is given):

pawn Код:
if (sscanf(params, "uiS[128]", giveplayerid, amount, reason)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /givecash <ID> <Amount> [Optional reason]");
Because we are no longer using "strtok" we no longer need "index" or "tmp" so they can be deleted.
We also don't need the "strtok" lines, or the lines checking if "tmp" is empty and if not converting
the data to numbers. You do, however, need to check that the parameters are valid for your uses.
For example, "i" returns an integer, so if a user types "12345" then "sscanf" is happy and will pass
- but the player may not have that much money:

pawn Код:
// Check the parameters are valid.
if (!IsPlayerConnected(giveplayerid)) return SendClientMessage(playerid, 0xFF0000AA, "Player not found.");
else if (amount < 0) return SendClientMessage(playerid, 0xFF0000AA, "You must enter a positive amount of money.");
else if (amount >= GetPlayerMoney(playerid)) return SendClientMessage(playerid, 0xFF0000AA, "You do not have that much money to give.");
There are still some improvements to make.
  1. "sscanf" sets a variable to "INVALID_PLAYER_ID" when you type something that looks like a name or
    ID (when using "u"), but they aren't connected.
  2. You don't need to check the length to see if a string is empty, you can just use "isnull".
  3. "SendClientMessageFormatted" exists in many forms and is very useful.
This makes the final command:

pawn Код:
CMD:givecash(playerid, params[])
{
    // Get all the parameters at once.
    new
        giveplayerid,
        amount,
        reason[128];
    if (sscanf(params, "uiS[128]", giveplayerid, amount, reason)) return SendClientMessage(playerid, 0xFF0000AA, "Usage : /givecash <ID> <Amount> [Optional reason]");
    // Check the parameters are valid.
    if (giveplayerid == INVALID_PLAYER_ID) return SendClientMessage(playerid, 0xFF0000AA, "Player not found.");
    if (amount < 0) return SendClientMessage(playerid, 0xFF0000AA, "You must enter a positive amount of money.");
    if (amount >= GetPlayerMoney(playerid)) return SendClientMessage(playerid, 0xFF0000AA, "You do not have that much money to give.");
    // Do the transfer.
    GivePlayerMoney(playerid, -amount);
    GivePlayerMoney(giveplayerid, amount);
    // Send messages (we have swapped the branches around).
    if (isnull(reason))
    {
        SendClientMessageFormatted(playerid, 0xFF0000AA, "You have sent $%d to %d for no reason", amount, giveplayerid);
        SendClientMessageFormatted(giveplayerid, 0xFF0000AA, "%d has sent you $%d for no reason", playerid, amount);
    }
    else
    {
        SendClientMessageFormatted(playerid, 0xFF0000AA, "You have sent $%d to %d for reason: %s", amount, giveplayerid, reason);
        SendClientMessageFormatted(giveplayerid, 0xFF0000AA, "%d has sent you $%d for reason: %s", playerid, amount, reason);
    }
    return 1;
}
Should I use one global string or lots of local ones?
Lots of local ones. Here is why:

pawn Код:
new
    gString[1024];

GetOnePlayerData(playerid)
{
    new
        Float:health,
        Float:armour;
    GetPlayerHealth(playerid, health);
    GetPlayerArmour(playerid, armour);
    format(gString, sizeof (gString), "Health: %.2f, Armour: %.2f, Score: %d, Cash: $%d",
        health,
        armour,
        GetPlayerScore(playerid),
        GetPlayerMoney(playerid));
    return gString;
}
That is great, and you decide to show the player their data:

pawn Код:
format(gString, sizeof (gString), "Your data: %s", GetOnePlayerData(playerid));
ShowPlayerDialog(playerid, 5050, DIALOG_STYLE_MSGBOX, "Your Data", gString, "OK", "");
Works perfectly, giving the expected output:

Код:
Your data: "Health: 100.00, Armour: 100.00, Score: 53, Cash: $898
Then you decide to show them EVERY player's data:

pawn Код:
gString[0] = '\0';
foreach (new i : Player)
{
    format(gString, sizeof (gString), "%s%d: %s\n", gString, i, GetOnePlayerData(i));
}
ShowPlayerDialog(playerid, 5050, DIALOG_STYLE_MSGBOX, "Player Data", gString, "OK", "");
Expected output:

Код:
1: "Health: 100.00, Armour: 80.00, Score: 2797, Cash: $364
2: "Health: 95.00, Armour: 0.00, Score: 845, Cash: $52
5: "Health: 100.00, Armour: 0.00, Score: 9784, Cash: $2456
6: "Health: 32.00, Armour: 0.00, Score: 678, Cash: $648
8: "Health: 100.00, Armour: 100.00, Score: 53, Cash: $898
Actual output:

Код:
"Health: 100.00, Armour: 100.00, Score: 53, Cash: $8988: "Health: 100.00, Armour: 100.00, Score: 53, Cash: $898
Have fun figuring out WHY! In short: don't use global strings!

I heard "X" is slow, is it?
NOTE: X means anything here.

We don't know!
  • Computers run millards (that's billions to you Americans) of instructions every second, they can do A LOT, so even slow things can be done very quickly.
  • If your function is only run once an hour, does it even MATTER is it is slow? A function that takes 1 millisecond to execute (and that is VERY slow to a computer), but only runs once every 3600000 milliseconds (that's an hour BTW) is only 0.000027% of your total code being run. Who CARES if it is slow? If you improve that code 1000x so it takes just one microsecond to run, your total code execution time has improved by an insignificant amount too tiny to even measure!
  • Before worrying about whether "switch" is faster than "if", just remember that if you find out and change, the total amount time you will save by using the better code is less than the time it took you to read this sentence.
How to convert to MySQL ?
First of all, You should know that SQL is a new language.. So I suggest you learn it first (Its pretty simple, Believe me).
There is a lot of free tutorials on internet, Just ******.

After that follow this great tutorial here: https://sampforum.blast.hk/showthread.php?tid=485633
It will help you setup MySQL on your computer and create your first MySQL script which will teach you how to use MySQL in PAWN. (Assuming you already know MySQL language now).

Then, You should be ready to go and convert your gamemode.
To be honest, There isn't a certain syntax to be used.. Just delete the whole current saving/loading system(Always backup first) then recode it the same way you learnt in the previous tutorial.
Reply
#2

Quote:

How do I convert y_commands to ZCMD?

Why would anyone need this?

Also, YCMD have already a defination to convert ZCMD to y_commands within the include. It won't give any warns or errors if you have CMD:command(playerid, params[]). But you can't add alternatives for commands like:
pawn Код:
CMD:commandalt(playerid, params[]) return cmd_command(playerid, params);
Quote:

How do I convert strcat to sscanf?

That must be strtok instead of strcat!
Reply
#3

Quote:
Originally Posted by Gammix
Посмотреть сообщение
Why would anyone need this?

Also, YCMD have already a defination to convert ZCMD to y_commands within the include. It won't give any warns or errors if you have CMD:command(playerid, params[]). But you can't add alternatives for commands like:
pawn Код:
CMD:commandalt(playerid, params[]) return cmd_command(playerid, params);

That must be strtok instead of strcat!
1) Its already written there that there is no need to convert and that both use the same syntax.

2) You are right, Edited.
Reply
#4

Good tutorial
Reply
#5

Very good tutorial
Reply
#6

AFAIK
for y_commands ( the name is it, not Y_CMD) actually you can do this

Код:
Command_ReProcess(playerid, /yourcommand parameter, help) - Use this if you want to call command from function, callback, etc.

Command_AddAltNamed(old[], altname[]) - Use this if you want multiple name in 1 command.
Reply
#7

This gonna be stick, I believe
Nice tutorial
Reply
#8

Added "How to convert to mysql ?"

All types of feedback and suggestions are accepted.
Reply
#9

About cmdtext and index.

Just use cmdtext instead of params.
CMD:heal(playerid, cmdtext[])

And make index global variable. And set it to default value after command finishes (there is a callback for that ... at least zcmd has it).
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)