[Tutorial] Saving system + commands - YSI includes, Whirlpool, foreach, sscanf2 0.3c
#1

Introduction
  • First of all, this will not work for SA:MP 0.3d. I've seen numerous people asking how to make an saving system but also add some commands. This tutorial will teach you how you can create a simple file saving system using y_ini, y_timers, foreach and whirlpool and also create some commands using y_commands and sscanf. I know there is already a tutorial on how to create a registration & login system using y_ini, by Kush.
So what's the difference between this and Kush tutorial?
  • The difference is that this tutorial will teach how you can hash the users passwords using Whirlpool, which is the safest method. Also, as I said above, it will teach you how you can create some basic commands using y_commands and sscanf.
What do you needDownloading
  • Download YSI Server Includes.
  • Download Whirlpool.
  • Download sscanf2.
  • Download foreach.
  • Copy/Cut the "YSI" folder into your "..pawno\include" folder.
  • Copy/Cut the "sscanf2.inc file into your "..pawno\include" folder.
  • Copy/Cut the "foreach.inc file into your "..pawno\include" folder.
Windows
  • Copy/Cut the "Whirlpool.dll file in your "plugins" folder. If you don't have one, create it.
  • Copy/Cut the "sscanf.dll file in your "plugins" folder.
  • Open "server.cfg", add "Whirlpool" and "sscanf" in the line "plugins".
Код:
plugins Whirlpool sscanf
Linux
  • Copy/Cut the "Whirlpool.so" file in your "plugins folder. If you don't have one, create it.
  • Copy/Cut the "sscanf.so file in your "plugins" folder.
  • Open "server.cfg", add "Whirlpool.so" and "sscanf.so" in the line "plugins".
Код:
plugins Whirlpool.so sscanf.so
Starting

We need to include sscanf2, y_ini, y_timers, foreach and y_commands, so at the top of our script:

pawn Код:
#include <a_samp>

#include <YSI\y_ini>
#include <YSI\y_commands>
#include <YSI\y_timers>
#include <foreach>
#include <sscanf2>
#include directive inserts the contents of the specified file at the current position within the current file. It will load all the codes from "..pawno\include\sscanf2.inc, "..pawno\include\YSI\y_ini.inc", "..pawno\include\YSI\y_timers.inc", "..pawno\include\foreach.inc" and "..pawno\include\YSI\y_commands.inc" into your script and that's what we need so we can load sscanf2, y_ini, y_timers, foreach and y_commands features. As you see, Whirlpool hasn't got an .inc file, so we just need to add it's native, somewhere after the includes:

pawn Код:
native WP_Hash(_buffer[], len, const str[]);
A native function is one defined in the virtual machine (the thing which runs the script), not in the script itself. You can only create native functions if they're coded into SA:MP or a plugin, however you can create fake natives, I will not go into details. We will use y_commands and sscanf2 later for creating commands. Let's define some colors and dialog id's, somewhere after the native:

pawn Код:
#define DIALOG_REGISTER 1
#define DIALOG_LOGIN 2

#define COLOR_WHITE "{FFFFFF}"
#define COLOR_RED "{F81414}"
#define COLOR_GREEN "{00FF22}"
#define directive defines a text substitution macro, a symbol replacement, wherever the first symbol of the define is found the rest of it will be placed. We need to store the users variables in an enum, enumerations aka enums are a very useful system for representing large groups of data and modifying constants quickly. There are a few main uses - replacing large sets of define statements, symbolically representing array slots (these are actually the same thing but they look different) and creating new tags. We will store the admin level, money, score and deaths, and the password we save it as an array 129 cells big.

pawn Код:
enum USER_ENUM { Password[ 129 ], Score, Deaths, Money, Admin_Level }
new
    userData[ MAX_PLAYERS ][ USER_ENUM ];
An array is a variable in which you can store multiple pieces of data at once and access them dynamically. We also declare a new two dimensional array called userData. Let's create a simple stock that will return the directory where the users accounts will be saved. A stock function is a function that the PAWN parser must plug into the program when it is used and that it may simply "remove" from the program (without warning) when it is not used. To declare a stock function, prefix the function name with the keyword stock. Public functions and native functions cannot be declared 'stock'.

pawn Код:
stock user_account_path(playerid)
{
    new
        string_path[ 128 ],
        player_name[ MAX_PLAYER_NAME ];
       
    GetPlayerName( playerid, player_name, MAX_PLAYER_NAME );
    format( string_path, sizeof ( string_path ), "%s.ini", player_name);
    return
        string_path;
}
We declare an array called string_path 128 cells big and another one called player_name MAX_PLAYER_NAME cells big (which means 24(maximum player name lenght in SA:MP )). We get the playerid name using GetPlayerName function and store the player name in the variable called player_name, we format the string and store it in the variable string_path, at the end we return the string_path. Since the string is mentioned as "%s.ini", the users accounts will be saved in your "..scriptfiles" directory.

For example, if you want the users accounts to be saved in "..scriptfiles/Accounts" make sure you change the string in the stock example:
pawn Код:
format( string_path, sizeof ( string_path ), "Accounts/%s.ini", player_name);
and also make sure you create the mentioned directory in your scriptfiles (example: "Accounts"), let's create a simple public function to load the users statistics:

pawn Код:
forward @load_user_statistics(playerid, name[], value[]);

@load_user_statistics(playerid, name[], value[])
{
    INI_String("Password", userData[ playerid ][ Password ], 129);
    INI_Int("Money", userData[ playerid ][ Money ]);
    INI_Int("AdminLevel", userData[ playerid ][ Admin_Level ]);
    INI_Int("Deaths", userData[ playerid ][ Deaths ]);
    INI_Int("Score", userData[ playerid ][ Score ]);
    return 1;
}
Public functions can be prefixed by either public or @, the @ character, when used, becomes part of the function name. INI_String saves the passed value as a string if the name matches, INI_Int - the data is passed to the loading callback as a string, this will convert it to an integer. Let's scroll down to OnPlayerConnect callback, we must check whenever a user connects to the server, if he is registered or not:

pawn Код:
public OnPlayerConnect(playerid)
{
    if( fexist ( user_account_path ( playerid ) ) )
    {
        INI_ParseFile(user_account_path ( playerid ), "load_user_%s", .bExtra = true, .extra = playerid);
        ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,"Welcome.Please log-in",""COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to log-in", #Log-in, #Quit);
    }  
    else
    {
        ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_INPUT,"Please register!",""COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to register.", #Register, #Quit);
    }
    userData[ playerid ][ Admin_Level ] = 0;
    userData[ playerid ][ Money ] = 0;
    userData[ playerid ][ Deaths ] = 0;
    userData[ playerid ][ Score ] = 0;
    return 1;
}
OnPlayerConnect callback is called when a player connects to the server, playerid is the ID of the player that connected.

fexist function checks if a specific file exists in your scriptfiles directory, in this case the user file. We use our stock as the parameter. If the file exist, we use INI_ParseFile to parse his file (load his statistics) and the user will be prompted to a dialog telling him to login. "Else" if the user file don't exist, he will be prompted to another dialog telling him to register, as you see we also added some embedded colors we defined earlier. After that we reset the user variables to 0. So basically it will just load the user password. Let's scroll down to "OnDialogResponse" callback:

pawn Код:
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    switch( dialogid )
    {
        case DIALOG_REGISTER:
        {
            if (!response) return Kick( playerid );
            if ( response )
            {
                if( !strlen ( inputtext ) ) return ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_INPUT,""COLOR_WHITE"Welcome.Please log-in","You have entered an "COLOR_RED"invalid"COLOR_WHITE" password\n"COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to log-in", #Register, #Quit);
               
                new
                    hashed_password[ 129 ];
                WP_Hash( hashed_password, sizeof ( hashed_password ), inputtext );
               
                new INI:File = INI_Open( user_account_path( playerid ) );
                INI_SetTag(File, "statistics" );
                INI_WriteString(File, "Password", hashed_password );
                INI_WriteInt(File, "Money", 0 );
                INI_WriteInt(File, "AdminLevel", 0 );
                INI_WriteInt(File, "Deaths", 0 );
                INI_WriteInt(File, "Score", 0 );
                INI_Close(File);
                SetSpawnInfo( playerid, 0, 0, 1958.33, 1343.12, 15.36, 269.15, 0, 0, 0, 0, 0, 0 );
                SpawnPlayer( playerid );            
            }
        }
        case DIALOG_LOGIN:
        {
            if ( !response ) return Kick ( playerid );
            if( response )
            {
                new
                    hashed_password[ 129 ];
                WP_Hash( hashed_password, sizeof ( hashed_password ), inputtext );
               
                if( !strcmp ( hashed_password, userData[ playerid ][ Password ] ) )
                {
                    INI_ParseFile(user_account_path ( playerid ), "load_user_%s", .bExtra = true, .extra = playerid);
                    GivePlayerMoney( playerid, userData[ playerid ][ Money ] );
                    SetPlayerScore( playerid, userData[ playerid ][ Score ] );
                }
                else
                {
                    ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,""COLOR_WHITE"Login",""COLOR_RED"You have entered an incorrect password.\n"COLOR_WHITE"Type your password below to login.","Login","Quit");
                }
                return 1;
            }
        }
    }
    return 1;
}
OnDialogResponse callback is called when a player presses any of the buttons on the dialog created using ShowPlayerDialog, playerid is the id of the player who responded to the dialog box, dialogid is the id of the dialog the player responded to, assigned in ShowPlayerDialog, response it's 1 for first button and 0 for second button, listitem is the id of the list item selected by the player, inputtext[] is the text entered into the input box by the player or the selected list item text.

Instead of using if statements to check the dialogs, I used cases as they are faster. We switch through all our dialogs. A switch statement is basically a structured if/else if/else system. We check if DIALOG_REGISTER is prompted. The if(!response) checks if the user press the second button (Quit), if so it will kick the user. We check if the user press the first button (Register) using if(response). The if(!strlen(inputtext)) will check if NOTHING has been entered in the dialog box, if so the user will be prompted to another dialog telling him the password is INCORRECT. We declare an array called hashed_password which will be the buffer we will pass of at least 129 characters. We use the whirlpool function to hash the user inputtext (which means the text that has been entered in the dialog box). Whirlpool will generate a hashed text and store it in the array called hashed_password. INI_Open will open the user file. INI_WriteString will write the user password stored in the array hashed_password ( so this means the hashed one ), and INI_WriteInt will write the another integers ( adminlevel, money, deaths, score ). After everything has been written, INI_Close will close the user file. We set the user spawn using SetSpawnInfo function and we spawn the user.

Same to DIALOG_LOGIN. We declare again the array called hashed_password, we hash the user inputtext using the whirlpool function, whirlpool will generate a hashed text and store it in the array called hashed_password. Strcmp compares two strings to see if they're the same. In our case, we compare the hashed text stored in the hashed_password variable with the hashed user password stored in the Password variable (which has been loaded from the user file under onplayerconnect callback). If the two strings are the same (user enters correct password), INI_ParseFile will load again the user statistics, we give the user his money using GivePlayerMoney function and set his score using SetPlayerScore function. Else if the two strings don't match, the user will be prompted to a dialog telling him he entered the wrong password, after the user disconnects, we need to re-write his stats that might have changed during the game time so let's scroll down to OnPlayerDisconnect callback:

pawn Код:
public OnPlayerDisconnect(playerid, reason)
{
    new INI:File = INI_Open( user_account_path ( playerid ) );
    INI_SetTag(File, "statistics" );
    INI_WriteInt(File, "Money", GetPlayerMoney( playerid ) );
    INI_WriteInt(File, "AdminLevel", userData[ playerid ][ Admin_Level ] );
    INI_WriteInt(File, "Deaths", userData[ playerid ][ Deaths ] );
    INI_WriteInt(File, "Score", GetPlayerScore( playerid ) );
    INI_Close(File);
    return 1;
}
OnPlayerDisconnect callback is called when a player disconnects from the server, playerid is the id of the player who left and reason id of the reason why they left.

We open the user file using "INI_Open" and our stock as parameter. We write to his "Money" the current money he has when he disconnects, using GetPlayerMoney function as the parameter. We write to his "Score" the current score he has when he disconnects using GetPlayerScore function as the parameter. We write to his "AdminLevel, Deaths" the value stored in the variables (Admin_Level, Deaths). We also need to update the user "Deaths" variable, and increase the user score on each kill, so we scroll down to "OnPlayerDeath" callback:

pawn Код:
public OnPlayerDeath(playerid, killerid, reason)
{
    if( killerid != INVALID_PLAYER_ID ) SetPlayerScore( killerid, GetPlayerScore( killerid ) + 1 );
   
    userData[ playerid ][ Deaths ] ++;
   
    return 1;
}
OnPlayerDeath callback is called when a player dies, playerid is the id of the player who died, killerid is the id of the player who killed the player who died, or INVALID_PLAYER_ID if there was none, reason is the id of the reason for the player's death.

We check if killerid isn't equal to INVALID_PLAYER_ID, if so we increase the killerid score with 1 using SetPlayerScore function and also GetPlayerScore function as one of the parameters. After that we increase the playerid deaths. Operator "!=" means "Left is not equal to Right", if ( Left != Right )

Your script should look something like this at the moment:
pawn Код:
// This is a comment
// uncomment the line below if you want to write a filterscript
//#define FILTERSCRIPT

#include <a_samp>
#include <YSI\y_ini>
#include <YSI\y_commands>
#include <YSI\y_timers>
#include <foreach>
#include <sscanf2>

native WP_Hash(_buffer[], len, const str[]);

#define DIALOG_REGISTER 1
#define DIALOG_LOGIN 2

#define COLOR_WHITE "{FFFFFF}"
#define COLOR_RED "{F81414}"
#define COLOR_GREEN "{00FF22}"

enum USER_ENUM { Password[ 129 ], Score, Deaths, Money, Admin_Level }
new
    userData[ MAX_PLAYERS ][ USER_ENUM ];

stock user_account_path(playerid)
{
    new
        string_path[ 128 ],
        player_name[ MAX_PLAYER_NAME ];
       
    GetPlayerName( playerid, player_name, MAX_PLAYER_NAME );
    format( string_path, sizeof ( string_path ), "%s.ini", player_name);
    return
        string_path;
}

forward @load_user_statistics(playerid, name[], value[]);

@load_user_statistics(playerid, name[], value[])
{
    INI_String("Password", userData[ playerid ][ Password ], 129);
    INI_Int("Money", userData[ playerid ][ Money ]);
    INI_Int("AdminLevel", userData[ playerid ][ Admin_Level ]);
    INI_Int("Deaths", userData[ playerid ][ Deaths ]);
    INI_Int("Score", userData[ playerid ][ Score ]);
    return 1;
}

#if defined FILTERSCRIPT

public OnFilterScriptInit()
{
    print("\n--------------------------------------");
    print(" Blank Filterscript by your name here");
    print("--------------------------------------\n");
    return 1;
}

public OnFilterScriptExit()
{
    return 1;
}

#else

main()
{
    print("\n----------------------------------");
    print(" Blank Gamemode by your name here");
    print("----------------------------------\n");
}

#endif

public OnGameModeInit()
{
    // Don't use these lines if it's a filterscript
    SetGameModeText("Blank Script");
    AddPlayerClass(0, 1958.3783, 1343.1572, 15.3746, 269.1425, 0, 0, 0, 0, 0, 0);
    return 1;
}

public OnGameModeExit()
{
    return 1;
}

public OnPlayerRequestClass(playerid, classid)
{
    SetPlayerPos(playerid, 1958.3783, 1343.1572, 15.3746);
    SetPlayerCameraPos(playerid, 1958.3783, 1343.1572, 15.3746);
    SetPlayerCameraLookAt(playerid, 1958.3783, 1343.1572, 15.3746);
    return 1;
}

public OnPlayerConnect(playerid)
{
    if( fexist ( user_account_path ( playerid ) ) )
    {
        INI_ParseFile(user_account_path ( playerid ), "load_user_%s", .bExtra = true, .extra = playerid);
        ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,"Welcome.Please log-in",""COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to log-in", #Log-in, #Quit);
    }  
    else
    {
        ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_INPUT,"Please register!",""COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to register.", #Register, #Quit);
    }
    userData[ playerid ][ Admin_Level ] = 0;
    userData[ playerid ][ Money ] = 0;
    userData[ playerid ][ Deaths ] = 0;
    userData[ playerid ][ Score ] = 0;
    return 1;
}

public OnPlayerDisconnect(playerid, reason)
{
    new INI:File = INI_Open( user_account_path ( playerid ) );
    INI_SetTag(File, "statistics" );
    INI_WriteInt(File, "Money", GetPlayerMoney( playerid ) );
    INI_WriteInt(File, "AdminLevel", userData[ playerid ][ Admin_Level ] );
    INI_WriteInt(File, "Deaths", userData[ playerid ][ Deaths ] );
    INI_WriteInt(File, "Score", GetPlayerScore( playerid ) );
    INI_Close(File);
    return 1;
}

public OnPlayerSpawn(playerid)
{
    return 1;
}

public OnPlayerDeath(playerid, killerid, reason)
{
    if( killerid != INVALID_PLAYER_ID ) SetPlayerScore( killerid, GetPlayerScore( killerid ) + 1 );
   
    userData[ playerid ][ Deaths ] ++;
   
    return 1;
}

public OnVehicleSpawn(vehicleid)
{
    return 1;
}

public OnVehicleDeath(vehicleid, killerid)
{
    return 1;
}

public OnPlayerText(playerid, text[])
{
    return 1;
}

public OnPlayerCommandText(playerid, cmdtext[])
{
    if (strcmp("/mycommand", cmdtext, true, 10) == 0)
    {
        // Do something here
        return 1;
    }
    return 0;
}

public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
{
    return 1;
}

public OnPlayerExitVehicle(playerid, vehicleid)
{
    return 1;
}

public OnPlayerStateChange(playerid, newstate, oldstate)
{
    return 1;
}

public OnPlayerEnterCheckpoint(playerid)
{
    return 1;
}

public OnPlayerLeaveCheckpoint(playerid)
{
    return 1;
}

public OnPlayerEnterRaceCheckpoint(playerid)
{
    return 1;
}

public OnPlayerLeaveRaceCheckpoint(playerid)
{
    return 1;
}

public OnRconCommand(cmd[])
{
    return 1;
}

public OnPlayerRequestSpawn(playerid)
{
    return 1;
}

public OnObjectMoved(objectid)
{
    return 1;
}

public OnPlayerObjectMoved(playerid, objectid)
{
    return 1;
}

public OnPlayerPickUpPickup(playerid, pickupid)
{
    return 1;
}

public OnVehicleMod(playerid, vehicleid, componentid)
{
    return 1;
}

public OnVehiclePaintjob(playerid, vehicleid, paintjobid)
{
    return 1;
}

public OnVehicleRespray(playerid, vehicleid, color1, color2)
{
    return 1;
}

public OnPlayerSelectedMenuRow(playerid, row)
{
    return 1;
}

public OnPlayerExitedMenu(playerid)
{
    return 1;
}

public OnPlayerInteriorChange(playerid, newinteriorid, oldinteriorid)
{
    return 1;
}

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
    return 1;
}

public OnRconLoginAttempt(ip[], password[], success)
{
    return 1;
}

public OnPlayerUpdate(playerid)
{
    return 1;
}

public OnPlayerStreamIn(playerid, forplayerid)
{
    return 1;
}

public OnPlayerStreamOut(playerid, forplayerid)
{
    return 1;
}

public OnVehicleStreamIn(vehicleid, forplayerid)
{
    return 1;
}

public OnVehicleStreamOut(vehicleid, forplayerid)
{
    return 1;
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    switch( dialogid )
    {
        case DIALOG_REGISTER:
        {
            if (!response) return Kick( playerid );
            if ( response )
            {
                if( !strlen ( inputtext ) ) return ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,""COLOR_WHITE"Welcome.Please log-in","You have enteCOLOR_RED an "COLOR_RED"invalid"COLOR_WHITE" password\n"COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to log-in", #Register, #Quit);
               
                new
                    hashed_password[ 129 ];
                WP_Hash( hashed_password, sizeof ( hashed_password ), inputtext );
               
                new INI:File = INI_Open( user_account_path( playerid ) );
                INI_SetTag(File, "statistics" );
                INI_WriteString(File, "Password", hashed_password );
                INI_WriteInt(File, "Money", 0 );
                INI_WriteInt(File, "AdminLevel", 0 );
                INI_WriteInt(File, "Deaths", 0 );
                INI_WriteInt(File, "Score", 0 );
                INI_Close(File);
                SetSpawnInfo( playerid, 0, 0, 1958.33, 1343.12, 15.36, 269.15, 0, 0, 0, 0, 0, 0 );
                SpawnPlayer( playerid );            
            }
        }
        case DIALOG_LOGIN:
        {
            if ( !response ) return Kick ( playerid );
            if( response )
            {
                new
                    hashed_password[ 129 ];
                WP_Hash( hashed_password, sizeof ( hashed_password ), inputtext );
               
                if( !strcmp ( hashed_password, userData[ playerid ][ Password ] ) )
                {
                    INI_ParseFile(user_account_path ( playerid ), "load_user_%s", .bExtra = true, .extra = playerid);
                    GivePlayerMoney( playerid, userData[ playerid ][ Money ] );
                    SetPlayerScore( playerid, userData[ playerid ][ Score ] );
                }
                else
                {
                    ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,""COLOR_WHITE"Login",""COLOR_RED"You have entered an incorrect password.\n"COLOR_WHITE"Type your password below to login.","Login","Quit");
                }
                return 1;
            }
        }
    }
    return 1;
}

public OnPlayerClickPlayer(playerid, clickedplayerid, source)
{
    return 1;
}
An user file should look something like this at the moment:

Код:
[statistics]
Password = 3F4811B9E6C37CE5900ADA5BC660C8632D13EA28A11DD0C54E3E0FD4C776F285AB1678C7FA708FF0078FB495BC05C09D654B063F07EB1859F3440E4B5F1B5449
Money = 0
AdminLevel = 0
Score = 0
Deaths = 0
Let's add the help system command in the bottom of our script, make sure the commands won't be inside any callback.

Explaining the "help" part

As you see every command has an help parameter, I find this help system very useful, the way it works is - imagine for example you have a /teleport command, a player wants to know what actually that command does before using it so instead of asking another players what the command do, he can simply type "/help teleport". It will display what you have specified for the help parameter on the /teleport command. Here is an example:


pawn Код:
YCMD:help( playerid, params[], help )
{
    if ( help ) return SendClientMessage( playerid, -1, #Displays help about a command );
    /* if help parameter occured (e.g player types "/help help") it will return the message above */
    if ( isnull ( params ) ) return SendClientMessage( playerid, -1, #Syntax: /help [command] );
    /* if parameters are null (e.g player types "/help") it will return the message above */
    Command_ReProcess(playerid, params, true);
    /* Call to put text through the command processor, with the help functions enabled or disabled */
    return 1;
}
YCMD:teleport( playerid, params[], help )
{
    if ( help ) return SendClientMessage( playerid, -1, #This command teleports you somewhere! );
    // Do your code
    return 1;
}
When the player types "/help teleport" it will send him the message above (This command teleports.. ). Another extraordinary thing is that y_commands has a special system, it knows every command you have so you don't have to update your /commands list! -

pawn Код:
YCMD:commands(playerid, params[], help)
{
    if ( help ) return SendClientMessage(playerid, -1, #Lists all the commands a player can use! );
    new
        count = Command_GetPlayerCommandCount( playerid );
    for ( new i = 0; i != count; ++i) SendClientMessage( playerid, -1, Command_GetNext ( i, playerid ) );
    return 1;
}
If parameter help occured (player types /help commands) it will return the above messaage. Command_GetPlayerCommandCount gets the number of commands in the system the player can use, Command_GetNext is used for looping through all the commands a player can use, not all the commands that exist. Another thing, when a player types a wrong command if you want to return the regular "SERVER: Unknown command" message or a custom message, simply replace y_commands.inc with:

Quote:
Originally Posted by ******
Посмотреть сообщение
This version has support for "OnPlayerCommandPerformed" and "OnPlayerCommandReceived" callbacks, so you can simply:

pawn Код:
/* custom message */
public OnPlayerCommandPerformed(playerid, cmdtext[], success)
{
    if( !success )
    {
        format( cmdtext, 128, "ERROR: Command %s does not exist, type /commands for all available commands", cmdtext );
        SendClientMessage( playerid, -1, cmdtext );
    }
    return 1;
}

/* regular Unknown message */
public OnPlayerCommandPerformed(playerid, cmdtext[], success)
{
    if( !success ) return false;
    return true;
}
To add an alternate spelling we use the function Command_AddAltNamed(old[], altname[]). Let's add an altername spelling to our /teleport command, something like /tp, if it's a filterscript, add the following code under OnFilterScriptInit callback, otherwise add it under OnGameModeInit callback.

pawn Код:
public OnFilterScriptInit()
{
    Command_AddAltNamed("teleport", "tp");
    return 1;
}
Adding commands

Let's add a setadmin command so we can promote another players to admin.

pawn Код:
YCMD:setadmin( playerid, params[], help )
{
    new
        TargetID, AdminLevel, PlayerName[ MAX_PLAYER_NAME ], TargetName[ MAX_PLAYER_NAME ], _message[ 128 ];
   
    if ( help ) return SendClientMessage( playerid, -1, #This command promotes someone to admin );
    if ( sscanf ( params, "ri", TargetID, AdminLevel ) ) return SendClientMessage( TargetID, -1, #Syntax /setadmin <playername/id> <level> );
    if ( TargetID == INVALID_PLAYER_ID ) return SendClientMessage( playerid, -1, #Player offline );
   
    GetPlayerName( playerid, PlayerName, sizeof ( PlayerName ) );
    GetPlayerName( TargetID, TargetName, sizeof ( TargetName ) );
   
    format( _message, sizeof ( _message ), "You have been promoted to admin level %d by %s", AdminLevel, PlayerName );
    SendClientMessage( TargetID, -1, _message );
   
    format( _message, sizeof ( _message ), "You have promoted %s to admin level %d", TargetName, AdminLevel );
    SendClientMessage( playerid, -1, _message );
   
    userData[ TargetID ][ Admin_Level ] = AdminLevel;
   
    return 1;
}
We declare some new variables. TargetID would be the ID of the promoted player, AdminLevel would be the level you promote the player to, array PlayerName 24 cells big would be the name of the playerid (player who types the command), array TargetName 24 cells big would be the name of the TargetID (player on which the command is executed), array _message 128 cells big will store some messages telling the both players the action.

If help parameter occured (player types /help setadmin) it will return the "This command promotes someone to admin" message.

We split our parameters using sscanf. We enlist 'r' and 'i'. The 'r' specifier stands for a player (in our case TargetID), the 'i' specifier stands for an integer (in our case AdminLevel). Sscanf will check if the player enteres the INCORRECT usage of the command, if so it will return the above syntax message. I'll show more specifiers after I'm finish explaining the command.

Operator '==' means 'left is equal to right' example: ' if ( left == right ) '. We check if TargetID (player on which we try to execute the command) is equal to INVALID_PLAYER_ID, if so it will return the message that the TargetID is offline.

We get the playerid (player who types the command) name using GetPlayerName function. sizeof operator returns the size of a variable in "elements". For a simple (non-compound) variable, the result of sizeof is always 1, because an element is a cell for a simple variable. It will return the size of the one dimension array called PlayerName, which is MAX_PLAYER_NAME, which is equal to 24, and it will store the player name in the PlayerName array.
We get the TargetID (player on which the command is executed) name using GetPlayerName function. It will return the size of the one dimension array called TargetName, which is MAX_PLAYER_NAME, which is equal to 24, and it will store the player name in the TargetName array.

We format our first message, which will be sent to the promoted player. Format function formats a string to include variables and other strings inside it. The sizeof( _message ) will return the size of the _message array, which in this case is 128. Since we cannot guess the player name who will execute the command and the admin level he will promote someone, we add some placeholders. Placeholder %d inserts an integer (whole) number, it will be a placeholder for the level the player will be promoted to (AdminLevel). Placeholder %s inserts a string, it will be a placeholder for the PlayerName (player who types the command). As you see, this message should be sent to the player who gets promoted so we use SendClientMessage function and send it to TargetID using -1 as the color.

We format our second message, which will be sent to the player who types the command. The sizeof( _message ) will return again the size of the _message array, which in this case is 128. The first %s placeholder will be for the TargetName (the player which has been promoted), the second %d placeholder will be for the AdminLevel (the level the player has been promoted to). As you see this message should be sent to playerid (player who types the command) so we use SendClientMessage function and send it to playerid using -1 as the color.

In the end of the command, operator '=' assigns a value. We assign to the variable Admin_Level of TargetID (player which has got promoted) the AdminLevel value, which would be the level he has been promoted to. We will not open again the player file and write data into it since the admin level will be stored in his variable (Admin_Level) and it will be automatically written when the player disconnects.

A list of the specifiers:

Quote:
Originally Posted by ******
Посмотреть сообщение
Код:
Format					Use
L(true/false)				Optional logical truthity
l					Logical truthity
B(binary)				Optional binary number
b					Binary number
N(any format number)			Optional number
n					Number
C(character)				Optional character
c					Character
I(integer)				Optional integer
i					Integer
D(integer)				Optional integer
d					Integer
H(hex value)				Optional hex number
h					Hex number
O(octal value)				Optional octal value
o					Octal value
F(float)				Optional floating point number
f					Floating point number
G(float/INFINITY/-INFINITY/NAN/NAN_E)	Optional float with IEEE definitions
g					Float with IEEE definitions
{					Open quiet section
}					Close quiet section
P<delimiter>				Invalid delimiter change
p<delimiter>				Delimiter change
Z(string)[length]			Invalid optional string
z(string)[length]			Deprecated optional string
S(string)[length]			Optional string
s[length]				String
U(name/id)				Optional user (bot/player)
u					User (bot/player)
Q(name/id)				Optional bot (bot)
q					Bot (bot)
R(name/id)				Optional player (player)
r					Player (player)
A<type>(default)[length]		Optional array of given type
a<type>[length]				Array of given type
E<specification>(default)		Optional enumeration of given layout
e<specification>			Enumeration of given layout
'string'				Search string
%					Deprecated optional specifier prefix
Sscanf formula in a command should be:

pawn Код:
if ( !sscanf ( params, "specifiers_here", variables_here ) ) /* if player enteres CORRECT usage of the command NOTE THE "!" */
{
    // Do your code
}
pawn Код:
if ( sscanf ( params, "specifiers_here", variables_here ) ) /* if player enteres INCORRECT usage of the command */
{
    // Do your code
}
Permissions

Now, what you want to do is allow only RCON administrators to use this command, we will set permissions using Command_SetPlayerNamed function. We need to set a timer since the player data might change during the game time, so we create a simple loop inside the timer and set some permissions.

pawn Код:
public OnFilterScriptInit()
{
    Command_AddAltNamed("teleport", "tp");
    repeat UpdatePermissions();
    return 1;
}

timer UpdatePermissions[1000]()
{
    foreach( Player, i )
    {
        if ( IsPlayerAdmin ( i ) )
        {
            Command_SetPlayerNamed("setadmin", i, true );
        }
        else
        {
            Command_SetPlayerNamed("setadmin", i, false );
        }
    }
}
We create a new timer called UpdatePermissions which will be called every 1000 milliseconds (1 second). We loop through all players using foreach, we check if a player is logged into RCON using IsPlayerAdmin function. If so, we enable the /setadmin command for him. Else if a player isn't logged into RCON, we disable the command for him. If it's a gamemode, just add the "repeat UpdatePermission()" under OnGameModeInit callback. Let's create a simple stats command that will display your statistics:

pawn Код:
YCMD:stats( playerid, params[], help )
{
    if ( help ) return SendClientMessage( playerid, -1, #This command displays your current statistics );
    if ( isnull ( params ) )
    {
        new
            sString[ 128 ],
            a_level = userData[ playerid ][ Admin_Level ],
            money = userData[ playerid ][ Money ],
            score = userData[ playerid ][ Score ],
            deaths = userData[ playerid ][ Deaths ];
           
        format (  sString, sizeof ( sString ), "Your stats are: Admin Level: %d | Money: %d | Score: %d | Deaths: %d", a_level, money, score, deaths );
        SendClientMessage( playerid, -1, sString );
    }
    return 1;
}
If parameter help occured (player types /help stats) it will return the message above (This command displays your current statistics).

We check if the parameters of the command are null (player types just /stats). We declare a new array called sString 128 cells big, a new variable called a_level and we assign it a value, so instead of using "userData[ playerid ][ Admin_Level ]" you can simply use "a_level", it's shorter. Same to another ones.

We format our message and store it in the variable called sString, the sizeof( sString ) will return the size of the array called sString which in this case it's 128. Since the admin level, money score and deaths are integers (numbers) we use the placeholder %d which inserts a integer. We use SendClientMessage to send our message to the player who types the command.

Now you can create your own commands !

Let's create a simple stock function wich will return the player admin level in WORDS. So for example if his Admin_Level variable is equal to 3, it will be also equal to "Head Admin".

pawn Код:
stock GetPlayerAdminLevel(playerid)
{
    new
        a_level_string[ 128 ];
       
    if ( userData[ playerid ][ Admin_Level ] == 1 )
        format( a_level_string, sizeof ( a_level_string ), "Little Admin" );
    else if ( userData[ playerid ][ Admin_Level ] == 2 )
        format( a_level_string, sizeof ( a_level_string ), "Medium Admin" );
    else if ( userData[ playerid ][ Admin_Level ] == 3 )
        format( a_level_string, sizeof ( a_level_string ), "Head Admin" );
    else
        format( a_level_string, sizeof ( a_level_string ), "Unknown admin level!" );
       
    return a_level_string;
}
I just named the stock GetPlayerAdminLevel (that's what came up in my mind). We declare an array called a_level_string 128 cells big.

We check if the playerid Admin_Level it's equal to 1, if so we format the string as "Little Admin".
We check if the playerid Admin_Level it's equal to 2, if so we format the string as "Medium Admin".
We check if the playerid Admin_Level it's equal to 3, if so we format the string as "Head Admin".
Else we format the string as "Unknown admin level!".

At the end of the stock, we return the a_level_string.

Now maybe you're asking what this actually may do and how you can use it so here is an example:

pawn Код:
YCMD:myadminlevel(playerid, params[], help)
{
    if ( help ) return SendClientMessage( playerid, -1, #This command displays your current admin level );
    if ( isnull ( params ) )
    {
        new
            sString[ 128 ];
       
        format( sString, sizeof ( sString ), "Your current admin level is: %s", GetPlayerAdminLevel(playerid) );
        SendClientMessage( playerid, -1, sString );
    }
    return 1;
}
If parameter help occured (player types /help myadminlevel) it will return the message above (This command displays your current admin level).

We check if the parameters of the command are null (player types just /myadminlevel). We declare a new array called sString 128 cells big.
We format our message and store it in the variable called sString. We use the placeholder %s (which inserts a string) for the function GetPlayerAdminLevel since it will return a string. So if the playerid Admin_Level variable it's equal to 1, it will return (Your current admin level is: Little Admin), if it's equal to 2 it will return (Your current admin level is: Medium Admin), if it's equal to 3 it will return (Your current admin level is: Head Admin), ELSE it will return (Unknown admin level!).
We use SendClientMessage to send the message to the playerid.

Last example

Last example I'll show you is how for example set the /stats command just for admin level 2:

pawn Код:
timer UpdatePermissions[1000]()
{
    foreach( Player, i )
    {
        if ( IsPlayerAdmin ( i ) )
        {
            Command_SetPlayerNamed("setadmin", i, true );
            Command_SetPlayerNamed("stats", i, false );
        }
        if ( userData[ i ][ Admin_Level ] == 2 )
        {
            Command_SetPlayerNamed("stats", i, true );
            Command_SetPlayerNamed("setadmin", i, false );
        }
        else
        {
            Command_SetPlayerNamed("setadmin", i, false );
            Command_SetPlayerNamed("stats", i, false );
        }
    }
}
If player is logged to RCON, we enable the /setadmin command for him but disable the /stats command. If player admin level is equal to 2 we enable the /stats command but disable the setadmin command. Else if this two conditions are not true we disable the both commands.

Result

Your script should look something like this:

pawn Код:
// This is a comment
// uncomment the line below if you want to write a filterscript
//#define FILTERSCRIPT

#include <a_samp>
#include <YSI\y_ini>
#include <YSI\y_commands>
#include <YSI\y_timers>
#include <foreach>
#include <sscanf2>

native WP_Hash(_buffer[], len, const str[]);

#define DIALOG_REGISTER 1
#define DIALOG_LOGIN 2

#define COLOR_WHITE "{FFFFFF}"
#define COLOR_RED "{F81414}"
#define COLOR_GREEN "{00FF22}"

enum USER_ENUM { Password[ 129 ], Score, Deaths, Money, Admin_Level }
new
    userData[ MAX_PLAYERS ][ USER_ENUM ];

stock user_account_path(playerid)
{
    new
        string_path[ 128 ],
        player_name[ MAX_PLAYER_NAME ];
       
    GetPlayerName( playerid, player_name, MAX_PLAYER_NAME );
    format( string_path, sizeof ( string_path ), "%s.ini", player_name);
    return
        string_path;
}

forward @load_user_statistics(playerid, name[], value[]);

@load_user_statistics(playerid, name[], value[])
{
    INI_String("Password", userData[ playerid ][ Password ], 129);
    INI_Int("Money", userData[ playerid ][ Money ]);
    INI_Int("AdminLevel", userData[ playerid ][ Admin_Level ]);
    INI_Int("Deaths", userData[ playerid ][ Deaths ]);
    INI_Int("Score", userData[ playerid ][ Score ]);
    return 1;
}

#if defined FILTERSCRIPT

public OnFilterScriptInit()
{
    print("\n--------------------------------------");
    print(" Blank Filterscript by your name here");
    print("--------------------------------------\n");
   
    Command_AddAltNamed("teleport", "tp");
    repeat UpdatePermissions();
    return 1;
}

public OnFilterScriptExit()
{
    return 1;
}

#else

main()
{
    print("\n----------------------------------");
    print(" Blank Gamemode by your name here");
    print("----------------------------------\n");
}

#endif

public OnGameModeInit()
{
    // Don't use these lines if it's a filterscript
    SetGameModeText("Blank Script");
    AddPlayerClass(0, 1958.3783, 1343.1572, 15.3746, 269.1425, 0, 0, 0, 0, 0, 0);
    return 1;
}

public OnGameModeExit()
{
    return 1;
}

public OnPlayerRequestClass(playerid, classid)
{
    SetPlayerPos(playerid, 1958.3783, 1343.1572, 15.3746);
    SetPlayerCameraPos(playerid, 1958.3783, 1343.1572, 15.3746);
    SetPlayerCameraLookAt(playerid, 1958.3783, 1343.1572, 15.3746);
    return 1;
}

public OnPlayerConnect(playerid)
{
    if( fexist ( user_account_path ( playerid ) ) )
    {
        INI_ParseFile(user_account_path ( playerid ), "load_user_%s", .bExtra = true, .extra = playerid);
        ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,"Welcome.Please log-in",""COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to log-in", #Log-in, #Quit);
    }  
    else
    {
        ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_INPUT,"Please register!",""COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to register.", #Register, #Quit);
    }
    userData[ playerid ][ Admin_Level ] = 0;
    userData[ playerid ][ Money ] = 0;
    userData[ playerid ][ Deaths ] = 0;
    userData[ playerid ][ Score ] = 0;
    return 1;
}

public OnPlayerDisconnect(playerid, reason)
{
    new INI:File = INI_Open( user_account_path ( playerid ) );
    INI_SetTag(File, "statistics" );
    INI_WriteInt(File, "Money", GetPlayerMoney( playerid ) );
    INI_WriteInt(File, "AdminLevel", userData[ playerid ][ Admin_Level ] );
    INI_WriteInt(File, "Deaths", userData[ playerid ][ Deaths ] );
    INI_WriteInt(File, "Score", GetPlayerScore( playerid ) );
    INI_Close(File);
    return 1;
}

public OnPlayerSpawn(playerid)
{
    return 1;
}

public OnPlayerDeath(playerid, killerid, reason)
{
    if( killerid != INVALID_PLAYER_ID ) SetPlayerScore( killerid, GetPlayerScore( killerid ) + 1 );
   
    userData[ playerid ][ Deaths ] ++;
   
    return 1;
}

public OnVehicleSpawn(vehicleid)
{
    return 1;
}

public OnVehicleDeath(vehicleid, killerid)
{
    return 1;
}

public OnPlayerText(playerid, text[])
{
    return 1;
}

public OnPlayerCommandText(playerid, cmdtext[])
{
    if (strcmp("/mycommand", cmdtext, true, 10) == 0)
    {
        // Do something here
        return 1;
    }
    return 0;
}

public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
{
    return 1;
}

public OnPlayerExitVehicle(playerid, vehicleid)
{
    return 1;
}

public OnPlayerStateChange(playerid, newstate, oldstate)
{
    return 1;
}

public OnPlayerEnterCheckpoint(playerid)
{
    return 1;
}

public OnPlayerLeaveCheckpoint(playerid)
{
    return 1;
}

public OnPlayerEnterRaceCheckpoint(playerid)
{
    return 1;
}

public OnPlayerLeaveRaceCheckpoint(playerid)
{
    return 1;
}

public OnRconCommand(cmd[])
{
    return 1;
}

public OnPlayerRequestSpawn(playerid)
{
    return 1;
}

public OnObjectMoved(objectid)
{
    return 1;
}

public OnPlayerObjectMoved(playerid, objectid)
{
    return 1;
}

public OnPlayerPickUpPickup(playerid, pickupid)
{
    return 1;
}

public OnVehicleMod(playerid, vehicleid, componentid)
{
    return 1;
}

public OnVehiclePaintjob(playerid, vehicleid, paintjobid)
{
    return 1;
}

public OnVehicleRespray(playerid, vehicleid, color1, color2)
{
    return 1;
}

public OnPlayerSelectedMenuRow(playerid, row)
{
    return 1;
}

public OnPlayerExitedMenu(playerid)
{
    return 1;
}

public OnPlayerInteriorChange(playerid, newinteriorid, oldinteriorid)
{
    return 1;
}

public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
{
    return 1;
}

public OnRconLoginAttempt(ip[], password[], success)
{
    return 1;
}

public OnPlayerUpdate(playerid)
{
    return 1;
}

public OnPlayerStreamIn(playerid, forplayerid)
{
    return 1;
}

public OnPlayerStreamOut(playerid, forplayerid)
{
    return 1;
}

public OnVehicleStreamIn(vehicleid, forplayerid)
{
    return 1;
}

public OnVehicleStreamOut(vehicleid, forplayerid)
{
    return 1;
}

public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    switch( dialogid )
    {
        case DIALOG_REGISTER:
        {
            if (!response) return Kick( playerid );
            if ( response )
            {
                if( !strlen ( inputtext ) ) return ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,""COLOR_WHITE"Welcome.Please log-in","You have enteCOLOR_RED an "COLOR_RED"invalid"COLOR_WHITE" password\n"COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to log-in", #Register, #Quit);
               
                new
                    hashed_password[ 129 ];
                WP_Hash( hashed_password, sizeof ( hashed_password ), inputtext );
               
                new INI:File = INI_Open( user_account_path( playerid ) );
                INI_SetTag(File, "statistics" );
                INI_WriteString(File, "Password", hashed_password );
                INI_WriteInt(File, "Money", 0 );
                INI_WriteInt(File, "AdminLevel", 0 );
                INI_WriteInt(File, "Deaths", 0 );
                INI_WriteInt(File, "Score", 0 );
                INI_Close(File);
                SetSpawnInfo( playerid, 0, 0, 1958.33, 1343.12, 15.36, 269.15, 0, 0, 0, 0, 0, 0 );
                SpawnPlayer( playerid );            
            }
        }
        case DIALOG_LOGIN:
        {
            if ( !response ) return Kick ( playerid );
            if( response )
            {
                new
                    hashed_password[ 129 ];
                WP_Hash( hashed_password, sizeof ( hashed_password ), inputtext );
               
                if( !strcmp ( hashed_password, userData[ playerid ][ Password ] ) )
                {
                    INI_ParseFile(user_account_path ( playerid ), "load_user_%s", .bExtra = true, .extra = playerid);
                    GivePlayerMoney( playerid, userData[ playerid ][ Money ] );
                    SetPlayerScore( playerid, userData[ playerid ][ Score ] );
                }
                else
                {
                    ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,""COLOR_WHITE"Login",""COLOR_RED"You have entered an incorrect password.\n"COLOR_WHITE"Type your password below to login.","Login","Quit");
                }
                return 1;
            }
        }
    }
    return 1;
}

public OnPlayerClickPlayer(playerid, clickedplayerid, source)
{
    return 1;
}

/*      OPTIONAL


public OnPlayerCommandPerformed(playerid, cmdtext[], success)
{
    if( !success )
    {
        format( cmdtext, 128, "ERROR: Command %s does not exist, type /commands for all available commands", cmdtext );
        SendClientMessage( playerid, -1, cmdtext );
    }
    return 1;
}


public OnPlayerCommandPerformed(playerid, cmdtext[], success)
{
    if( !success ) return false;
    return true;
}
*/


/* timers */

timer UpdatePermissions[1000]()
{
    foreach( Player, i )
    {
        if ( IsPlayerAdmin ( i ) )
        {
            Command_SetPlayerNamed("setadmin", i, true );
            Command_SetPlayerNamed("stats", i, false );
        }
        if ( userData[ i ][ Admin_Level ] == 2 )
        {
            Command_SetPlayerNamed("stats", i, true );
            Command_SetPlayerNamed("setadmin", i, false );
        }
        else
        {
            Command_SetPlayerNamed("setadmin", i, false );
            Command_SetPlayerNamed("stats", i, false );
        }
    }
}

/* stocks */

stock GetPlayerAdminLevel(playerid)
{
    new
        a_level_string[ 128 ];
       
    if ( userData[ playerid ][ Admin_Level ] == 1 )
        format( a_level_string, sizeof ( a_level_string ), "Little Admin" );
    else if ( userData[ playerid ][ Admin_Level ] == 2 )
        format( a_level_string, sizeof ( a_level_string ), "Medium Admin" );
    else if ( userData[ playerid ][ Admin_Level ] == 3 )
        format( a_level_string, sizeof ( a_level_string ), "Head Admin" );
    else
        format( a_level_string, sizeof ( a_level_string ), "Unknown admin level!" );
       
    return a_level_string;
}

/* commands */

YCMD:help( playerid, params[], help )
{
    if ( help ) return SendClientMessage( playerid, -1, #Displays help about a command );
    if ( isnull ( params ) ) return SendClientMessage( playerid, -1, #Syntax: /help [command] );
    Command_ReProcess(playerid, params, true);
    return 1;
}

YCMD:teleport( playerid, params[], help )
{
    if ( help ) return SendClientMessage( playerid, -1, #This command teleports you somewhere! );
    return 1;
}

YCMD:commands(playerid, params[], help)
{
    if ( help ) return SendClientMessage(playerid, -1, #Lists all the commands a player can use! );
    new
        count = Command_GetPlayerCommandCount( playerid );
    for ( new i = 0; i != count; ++i) SendClientMessage( playerid, -1, Command_GetNext ( i, playerid ) );
    return 1;
}

YCMD:setadmin( playerid, params[], help )
{
    new
        TargetID, AdminLevel, PlayerName[ MAX_PLAYER_NAME ], TargetName[ MAX_PLAYER_NAME ], _message[ 128 ];
   
    if ( help ) return SendClientMessage( playerid, -1, #This command promotes someone to admin );
    if ( sscanf ( params, "ri", TargetID, AdminLevel ) ) return SendClientMessage( TargetID, -1, #Syntax /setadmin <playername/id> <level> );
    if ( TargetID == INVALID_PLAYER_ID ) return SendClientMessage( playerid, -1, #Player offline );
   
    GetPlayerName( playerid, PlayerName, sizeof ( PlayerName ) );
    GetPlayerName( TargetID, TargetName, sizeof ( TargetName ) );
   
    format( _message, sizeof ( _message ), "You have been promoted to admin level %d by %s", AdminLevel, PlayerName );
    SendClientMessage( TargetID, -1, _message );
   
    format( _message, sizeof ( _message ), "You have promoted %s to admin level %d", TargetName, AdminLevel );
    SendClientMessage( playerid, -1, _message );
   
    userData[ TargetID ][ Admin_Level ] = AdminLevel;
   
    return 1;
}

YCMD:stats( playerid, params[], help )
{
    if ( help ) return SendClientMessage( playerid, -1, #This command displays your current statistics );
    if ( isnull ( params ) )
    {
        new
            sString[ 128 ],
            a_level = userData[ playerid ][ Admin_Level ],
            money = userData[ playerid ][ Money ],
            score = userData[ playerid ][ Score ],
            deaths = userData[ playerid ][ Deaths ];
           
        format (  sString, sizeof ( sString ), "Your stats are: Admin Level: %d | Money: %d | Score: %d | Deaths: %d", a_level, money, score, deaths );
        SendClientMessage( playerid, -1, sString );
    }
    return 1;
}

YCMD:myadminlevel(playerid, params[], help)
{
    if ( help ) return SendClientMessage( playerid, -1, #This command displays your current admin level );
    if ( isnull ( params ) )
    {
        new
            sString[ 128 ];
       
        format( sString, sizeof ( sString ), "Your current admin level is: %s", GetPlayerAdminLevel(playerid) );
        SendClientMessage( playerid, -1, sString );
    }
    return 1;
}
If you want any another commands to be explained, please reply.

References
Special thanks to
Reply
#2

Well Explained gratz Smit its big,well explained and i think that the most of people will read it clearly.. gratz :d
[offtopic]First?[/offtopic]
Reply
#3

Nice tutorial. Correctly explaned
Reply
#4

Awesome Tutorial SmiT
Really Nice Job!

Edit: I tested it, but I got some errors. How to fix them
pawn Код:
error 017: undefined symbol "repeat"
error 017: undefined symbol "UpdatePermissions"
//Line 55
    repeat UpdatePermissions();
pawn Код:
error 010: invalid function or declaration
//Line 153
timer UpdatePermissions[1000]()
//Line 155
    foreach( Player, i )
//Line 157
        if ( IsPlayerAdmin ( i ) )
pawn Код:
error 021: symbol already defined: "Command_SetPlayerNamed"
//Line 159
            Command_SetPlayerNamed("setadmin", i, true );
//Line 163
            Command_SetPlayerNamed("setlevel", i, false );
pawn Код:
error 010: invalid function or declaration
//Line 161
        else
Reply
#5

if( !strlen ( inputtext ) ) return ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_INPUT,""COLOR_WHITE"Welcome.Please log-in","You have enteCOLOR_RED an "COLOR_RED"invalid"COLOR_WHITE" password\n"COLOR_WHITE"Type your "COLOR_GREEN"password "COLOR_WHITE"here to log-in", #Register, #Quit)

thats wrong or? it says wrong password but its about to register
Reply
#6

Instead of
pawn Код:
"You have enter an "COLOR_RED"invalid"COLOR_WHITE" password\n"COLOR_WHITE"
He wrote this, by typing mistake
pawn Код:
"You have enteCOLOR_RED an "COLOR_RED"invalid"COLOR_WHITE" password\n"COLOR_WHITE"
Reply
#7

Quote:
Originally Posted by Kostas'
Посмотреть сообщение
Awesome Tutorial SmiT
Really Nice Job!

Edit: I tested it, but I got some errors. How to fix them

..code
Make sure you downloaded YSI 3.0 RC 2

Quote:
Originally Posted by Dripac
Посмотреть сообщение
code...

thats wrong or? it says wrong password but its about to register
Thanks for pointing out my typo.
Reply
#8

I download the newest version.
but
pawn Код:
fatal error 111: user error: "Old foreach.inc files are no longer compatible with YSI."
I downloaded the foreach from ******'s Thread here

Then I got these
pawn Код:
warning 235: public function lacks forward declaration (symbol "OnPlayerTakeDamage")
pawn Код:
warning 235: public function lacks forward declaration (symbol "OnPlayerGiveDamage")
Quote:
Originally Posted by ******
Посмотреть сообщение
Those are 0.3d callbacks, just ignore them if you're not on that.
I am using 0.3c. If update to 0.3d, I will need to update and things from my gamemode?
Reply
#9

EDIT: This will not work for 0.3d since it's using sscanf.
Reply
#10

Well, I download 0.3d but look at the picture

Ignore the Reaction Contest, isn't ready yet :P
Also, I set my self to head admin via Rcon and when I typed /myadminlevel, it appears "Your current admin level is: Unknown admin level!" I added 2 more levels up to 5.
pawn Код:
stock GetPlayerAdminLevel(playerid)
{
    new
        a_level_string[128];

    if (userData[playerid][Admin_Level] == 1)
        format(a_level_string, sizeof(a_level_string), "Help Operator");
    if (userData[playerid][Admin_Level] == 2 )
        format(a_level_string, sizeof(a_level_string), "Moderator");
    if (userData[playerid][Admin_Level] == 3 )
        format(a_level_string, sizeof(a_level_string), "Global Moderator");
    if (userData[playerid][Admin_Level] == 4 )
        format(a_level_string, sizeof(a_level_string), "Management");
    if (userData[playerid][Admin_Level] == 5 )
        format(a_level_string, sizeof(a_level_string), "Head Admin");
    else
        format(a_level_string, sizeof(a_level_string), "Unknown admin level!");

    return a_level_string;
}
Is anything wrong with it, or I should add the levels somewhere else too
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)