How to make basic commands
#1


How to make basic commands
Why I made this tutorial
I've saw many people who are trying things who are just impossible. First of all,
I want to note that its good to try stuff. Thats why I made this tutorial, people can read this and learn from it.
Also it might be some assistance in creating commands. You might think that there already is something like this,
but so far I don't like the tutorial on the wiki because that just gives the commands. People just copy it and don't
learn anything from it, and that is not the goal of a tutorial.




Getting started
In this tutorial I will explain how to create simple commands by your own. The big meaning behind this is how to
create messages with strings and integers and such. If you do not know what I'm talking about, please read this.




The tutorial
The first step
I will use dcmd in this tutorial, because it is shorter, faster and more simple to use. More information regarding
this can be found here. Before we continue we need the
code of dcmd. Put this line in the beginning of your script, above "main()".
pawn Code:
#define dcmd(%1,%2,%3) if (!strcmp((%3)[1], #%1, true, (%2)) && ((((%3)[(%2) + 1] == '\0') && (dcmd_%1(playerid, ""))) || (((%3)[(%2) + 1] == ' ') && (dcmd_%1(playerid, (%3)[(%2) + 2]))))) return 1


Define the command's name and length
To say the server what the command must be, we must use the following code in "OnPlayerCommandText(playerid,cmdtext[])".
pawn Code:
dcmd(cmd,3,cmdtext);
In this example "cmd" is the commands name, "3" is the length of the words, "cmdtext" is always the same - nothing really to explain about it. There you go, you just defined your command.



Creating the command
To say the server we're starting the command, we must use this.
Code:
dcmd_cmd(playerid,params[])
"cmd" Must be replaced with the command you defined at the earlier finished step. As you can see "params[]" became "cmdtext[]".

You have to open the command with brackets to define to the server we're going to put the code of the command here.


Indexing a variable and checking it for input
To a variable as params[] we have to make them connected to each other. This is called indexing. But watch out, this way is only for a command with one parameter. How to index commands with 2 or more parameters is explained in the next step.
pawn Code:
new action = strval(params);
This converts the variable params to a real integer, which can only be used as integer. This means you can only store numbers in it(commonly used for player ID's).

The next thing isn't really required, but I'll just tell it.
pawn Code:
new action[128] = params;
This makes "action" the string params. Now it can only be used as string.

This needs to be done at every command using 1 parameter.

Using strlen we can check if the person has entered the parameter correctly.
pawn Code:
if(!strlen(action)) return SendClientMessage(playerid,color,"You forgot to enter something!");
We can expand this, so it'll be more understandable.

Now we're going to check it for input.
pawn Code:
if(!strlen(action))
{
SendClientMessage(playerid,color,"You forgot to enter something!");
return 1;
}
Rough explaination: if action wasn't filled in return message: "You forgot to enter something!".
There you go, you just made your check against someone not filling in the command properly.


Indexing multiple variables and checking them for input
There are several ways to index multiple variables. I will only discribe the sscanf way, since sscanf is fast and easy.
This is the code of sscanf which you will need using this function. You can paste this anywhere in your script, except in a function or public.
pawn Code:
stock sscanf(string[], format[], {Float,_}:...)
{
    #if defined isnull
        if (isnull(string))
    #else
        if (string[0] == 0 || (string[0] == 1 && string[1] == 0))
    #endif
        {
            return format[0];
        }
    #pragma tabsize 4
    new
        formatPos = 0,
        stringPos = 0,
        paramPos = 2,
        paramCount = numargs(),
        delim = ' ';
    while (string[stringPos] && string[stringPos] <= &#39; ')
    {
        stringPos++;
    }
    while (paramPos < paramCount && string[stringPos])
    {
        switch (format[formatPos++])
        {
            case &#39;\0':
            {
                return 0;
            }
            case &#39;i', 'd':
            {
                new
                    neg = 1,
                    num = 0,
                    ch = string[stringPos];
                if (ch == &#39;-')
                {
                    neg = -1;
                    ch = string[++stringPos];
                }
                do
                {
                    stringPos++;
                    if (&#39;0' <= ch <= '9')
                    {
                        num = (num * 10) + (ch - &#39;0');
                    }
                    else
                    {
                        return -1;
                    }
                }
                while ((ch = string[stringPos]) > &#39; ' && ch != delim);
                setarg(paramPos, 0, num * neg);
            }
            case &#39;h', 'x':
            {
                new
                    ch,
                    num = 0;
                while ((ch = string[stringPos]) > &#39; ' && ch != delim)
                {
                    switch (ch)
                    {
                        case &#39;x', 'X':
                        {
                            num = 0;
                            continue;
                        }
                        case &#39;0' .. '9':
                        {
                            num = (num << 4) | (ch - &#39;0');
                        }
                        case &#39;a' .. 'f':
                        {
                            num = (num << 4) | (ch - (&#39;a' - 10));
                        }
                        case &#39;A' .. 'F':
                        {
                            num = (num << 4) | (ch - (&#39;A' - 10));
                        }
                        default:
                        {
                            return -1;
                        }
                    }
                }
                setarg(paramPos, 0, num);
            }
            case &#39;c':
            {
                setarg(paramPos, 0, string[stringPos++]);
            }
            case &#39;f':
            {
                setarg(paramPos, 0, _:floatstr(string[stringPos]));
            }
            case &#39;p':
            {
                delim = format[formatPos++];
                continue;
            }
            case &#39;\'':
            {
                new
                    end = formatPos - 1,
                    ch;
                while ((ch = format[++end]) && ch != &#39;\'') {}
                if (!ch)
                {
                    return -1;
                }
                format[end] = &#39;\0';
                if ((ch = strfind(string, format[formatPos], false, stringPos)) == -1)
                {
                    if (format[end + 1])
                    {
                        return -1;
                    }
                    return 0;
                }
                format[end] = &#39;\'';
                stringPos = ch + (end - formatPos);
                formatPos = end + 1;
            }
            case &#39;u':
            {
                new
                    end = stringPos - 1,
                    id = 0,
                    bool:num = true,
                    ch;
                while ((ch = string[++end]) && ch != delim)
                {
                    if (num)
                    {
                        if (&#39;0' <= ch <= '9')
                        {
                            id = (id * 10) + (ch - &#39;0');
                        }
                        else
                        {
                            num = false;
                        }
                    }
                }
                if (num && IsPlayerConnected(id))
                {
                    setarg(paramPos, 0, id);
                }
                else
                {
                    #if !defined foreach
                        #define foreach(%1,%2) for (new %2 = 0; %2 < MAX_PLAYERS; %2++) if (IsPlayerConnected(%2))
                        #define __SSCANF_FOREACH__
                    #endif
                    string[end] = &#39;\0';
                    num = false;
                    new
                        name[MAX_PLAYER_NAME];
                    id = end - stringPos;
                    foreach (Player, playerid)
                    {
                        GetPlayerName(playerid, name, sizeof (name));
                        if (!strcmp(name, string[stringPos], true, id))
                        {
                            setarg(paramPos, 0, playerid);
                            num = true;
                            break;
                        }
                    }
                    if (!num)
                    {
                        setarg(paramPos, 0, INVALID_PLAYER_ID);
                    }
                    string[end] = ch;
                    #if defined __SSCANF_FOREACH__
                        #undef foreach
                        #undef __SSCANF_FOREACH__
                    #endif
                }
                stringPos = end;
            }
            case &#39;s', 'z':
            {
                new
                    i = 0,
                    ch;
                if (format[formatPos])
                {
                    while ((ch = string[stringPos++]) && ch != delim)
                    {
                        setarg(paramPos, i++, ch);
                    }
                    if (!i)
                    {
                        return -1;
                    }
                }
                else
                {
                    while ((ch = string[stringPos++]))
                    {
                        setarg(paramPos, i++, ch);
                    }
                }
                stringPos--;
                setarg(paramPos, i, &#39;\0');
            }
            default:
            {
                continue;
            }
        }
        while (string[stringPos] && string[stringPos] != delim && string[stringPos] > &#39; ')
        {
            stringPos++;
        }
        while (string[stringPos] && (string[stringPos] == delim || string[stringPos] <= &#39; '))
        {
            stringPos++;
        }
        paramPos++;
    }
    do
    {
        if ((delim = format[formatPos++]) > &#39; ')
        {
            if (delim == &#39;\'')
            {
                while ((delim = format[formatPos++]) && delim != &#39;\'') {}
            }
            else if (delim != &#39;z')
            {
                return delim;
            }
        }
    }
    while (delim > &#39; ');
    return 0;
}
If you have pasted this in your script we can continue. Now we need to use the placeholders again. I'm first giving the example, then I will be explaining it. For example: You want to check the integer playerid, integer otherid and integer cash.
On the top of the script we create the integers.
pawn Code:
new otherid, cash;
There is no need for creating "playerid" because this was already implemented in the dcmd function.

Now we make the actual check and index those integers. Note that we this time don't have to use the % in front of the placeholders!
pawn Code:
if(sscanf(params,"ddd",playerid,otherid,cash)) return SendClientMessage(playerid,red,"USAGE: /sendcash [playerid] [otherid] [cash amount]");
Expanding it again..
pawn Code:
if(sscanf(params,"ddd",playerid,otherid,cash))
{
SendClientMessage(playerid,red,"USAGE: /sendcash [playerid] [otherid] [cash amount]");
return 1;
}
This command is obviously ment to send cash from player A to player B. Maybe its kind of a unessesary command, but I hope you will understand how sscanf works. If you enter a playername instead of a player's ID it will also return the usage message. Indexing automaticly happened, its done and you don't have to do anything more to prevent any errors relating the indexing. After this check you can make more of them. Like checking if the players are connected or not. If you do this before the sscanf, your pawn will give errors since your integers aren't indexed yet.



Using variables
String
To use strings into each other, you need to create it first. Remember that a string is for text only. My suggestion is: keep the name
as short as possible. Because of one simple reason: you don't have to type that much.
pawn Code:
new str[128];
Do not use a higher string-size! Ever! You will only need higher sizes when creating strings for MySQL cases. To read why, click here. In this string you can now put text. It is not nessesary to create a 128 sized string for a player name though. The player name won't be any longer then 28 characters at max, so 28 as string size should be enough.


Integer
To create an integer, you don't need any sizes. An integer is just plain text.
pawn Code:
new int;
In this you can now store numbers of any size.



Put two variables into each other
In this example I'm going to put .. into one string. To continue you have to know some important things about converting it into one.
Below I have listed all the placeholders you need to use. I've left the unimportant ones away.

Placeholder What is it
%d integer (whole number)
%f float (0.0000 for example)
%i integer (one number, used for loops mostly)
%s string (text (and numbers))
%z optional string (text (and numbers))
%% "%" (inserts the %)

pawn Code:
format(str,sizeof(str),"%s(%d) %s",pname,playerid,action);
%s is the player name, %d is the playerid and %s is the action. This is a example of a /me command. Executing this correctly would
turn this into the following result.
Quote:
Originally Posted by Server Output
Neoz(0) action
Please note that you always have to enter the amount of parameters you want to use, even if it is optional!


Examples
To just give you an idea, you could also use this with arrays. I'll take as example a check if the player is spawned.
pawn Code:
format(str,sizeof(str),"Check if %s is spawned: %d (0 = no / 1 = yes)",pname,pSpawned[playerid]);
In this example pSpawned[playerid] will be set to 1 at OnPlayerSpawn and set back to 0 at OnPlayerDeath.

You can also do it with strings to return a nice output. If you like nice outputs, you can store "yes" and "no" in the string. Though, if you want to
use this, the creation of the variable will be a bit more difficult. If you want to read more about that, I suggest you read this.

If you have followed the steps correctly you should now have a begin of the command, with some coding.



Finish the command
To finish the command, you have to put a return 1; in order to stop executing. Also you need a closing bracket to tell the server that the command has ended.

If you haven't used the params[] and didn't index anything(this is common(when making a teleport for example)) you need to put this between "return 1;" and "}".
pawn Code:
#pragma unused params
If you have done it right, you should now have a working command. See below for some explained examples.




Explained Examples
Example One: /me [action]
Defining the command at "OnPlayerText(playerid,cmdtext[])".
pawn Code:
dcmd(me,2,cmdtext);
Saying to the server that the command begins here
pawn Code:
dcmd_me(playerid,params[]);
Now you should tell the server we're starting the script: place a bracket.

Create the string for the players name and the string for the text which shows up using /me.
pawn Code:
new pname[24], str[128];
Store the name of the player into the string.
pawn Code:
GetPlayerName(playerid,pname,24);
Checking if the player entered "action".
pawn Code:
if(!strlen(params)) return SendClientMessage(playerid,0xAA3333AA,"USAGE: /me [action]");
Putting the two strings into each other
[pawno]format(str,sizeof(str),"%s %s",pname,action);[/pawn]

Sending the message to all the players and to the logs.
pawn Code:
SendClientMessageToAll(0xFFFF33AA,str);
print(str);
Now the command should be stopped in order to tell the server he must stop executing the command and tell him the command has stopped.


Example Two: /warn [playerid] [reason]
Defining the command at "OnPlayerText(playerid,cmdtext[])".
pawn Code:
dcmd(warn,4,cmdtext);
Saying to the server that the command begins here
pawn Code:
dcmd_warn(playerid,params[]);
Now you should tell the server we're starting the script: place a bracket.

Creating the variables.
pawn Code:
new pname[24], oname[24], otherid, str[128], str2[128];
Store the name of the player and the name of the other player into the string
pawn Code:
GetPlayerName(playerid,pname,24);
GetPlayerName(otherid,oname,24);
Checking if the player entered "otherid/playerid" and the optional "reason".
pawn Code:
if(sscanf(params,"dz",otherid,reason)) return SendClientMessage(playerid,0xFF0000AA,"USAGE: /warn [playerid] [optional reason]");
Checking if the entered playerid is connected.
pawn Code:
if(!IsPlayerConnected(otherid)) return SendClientMessage(playerid,0xFF0000AA,"That player isn't connected!");
Putting the two strings into one for both players
pawn Code:
format(str,sizeof(str),"You warned %s. Reason: %s",oname,reason);
format(str2,sizeof(str2),"You got warned by %s. Reason: %s",pname,reason);
Sending the messages to the players
pawn Code:
SendClientMessage(playerid,0xFFFF33AA,str);
SendClientMessage(otherid,0xFFFF33AA,str2);
Creating message for the logs
pawn Code:
printf("%s warned %s. Reason: %s",pname,oname,reason);
Now the command should be stopped in order to tell the server he must stop executing the command and tell him the command has stopped.




Final Word
I hope this tutorial helped you, at least assisted, in creating a command. If you got any questions, please ask them by PM so the topic keeps clean.




Bugs or mistakes
Please report any bugs or mistakes, because I'm busy with this tutorial for 3 hours already and I can't see more of it.
I'm willing to add more stuff in this tutorial if you have any suggestions.

Regards,
иєσz
Reply
#2

Very useful for beginners
Good Job
Reply
#3

Very nice for beginners! Very Nice!

Good Job.

This should be sticked ;P
Reply
#4

Quote:
Originally Posted by Miokie*
This should be sticked ;P
You can find thousands of tutorials on the wiki, no point of sticking this.
Reply
#5

Quote:
Originally Posted by tr0y
Quote:
Originally Posted by Miokie*
This should be sticked ;P
You can find thousands of tutorials on the wiki, no point of sticking this.
Yeah but this tutorial goes into greater depths and if its stickied here it would be seen more by newer scripters as they don't know what the wiki is.
Reply
#6

very helpful for teaching new scripters
Reply
#7

Might have been cooler if you introduced other methods alongside DCMD, like strtok and strcmp+strfind.
Reply
#8

Quote:
Originally Posted by SilentHuntR
Might have been cooler if you introduced other methods alongside DCMD, like strtok and strcmp+strfind.
i agree with SilentHuntR
Reply
#9

good for the noobs.. now only if they would learn how to SEARCH
Reply
#10

Quote:
Originally Posted by cj101
good for the noobs.. now only if they would learn how to SEARCH
its hard for them to use. it takes great skill to type words into a little white box!
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)