mardi 19 juillet 2016

[Tutorial] [MySQL/SQlite] Login and register script with EasyDB

Let's block ads! (Why?)



[Tutorial] [MySQL/SQlite] Login and register script with EasyDB

High-roller

 
Gammix's Avatar
 

Join Date: Jan 2015

Location: Ottawa, Canada

Posts: 1,244

Reputation: 416

Default[MySQL/SQlite] Login and register script with EasyDB

Login and register
MySQL/SQLite both supported with one line change
EasyDB

Hi, there since i am now only supporting EasyDB and no other of my SQL includes (deleted now); I decided to make a tutorial of a basic login and register system with dialogs and couple of commands.

Following tutorial covers SQL based database and since we are using EasyDB, you can switch to any database type i.e. SQLite or MySQL anytime by editing just one line of code and you'll be done. One of the greatest advantage of EasyDB.

What this tutorial covers?

  • Links to all requirements
  • Scripting section, writing the code of login and register system
  • Finish (overall script sample)

Requirements
Scripting (main part)
  • SCRIPT TYPE
    First of all you should decide if you are creating a filterscript or a gamemode, because a lot of libraries depends on the script's type. So
    this is really a good practice and really appreciated by scripters if you define FILTERSCRIPT in your script's top if your script is a filterscript.

    So in case you are using a filterscript, add this in the very top:

    Code:

    #define FILTERSCRIPT // add this your script is a filterscript
    

  • INCLUDING LIBRARIES
    Now here is a simple part where you decide you want SQLite or MySQL.

    First we would add <a_samp>:

    Code:

    #include <a_samp>
    
    // rest of the libraries goes here
    

    For MySQL users:

    Code:

    #define CONNECTION_TYPE_MYSQL // tells "easydb" that we are using mysql
    #define ENABLE_CONSOLE_MESSAGES // tells "easydb" that we would need console messages/debugs while run time to keep checks and detect if any errors/warnings
    
    #include <easydb>
    #include <izcmd>
    #include <sscanf2>
    

    For SQLite users:

    Code:

    #define ENABLE_CONSOLE_MESSAGES // tells "easydb" that we would need console messages/debugs while run time to keep checks and detect if any errors/warnings
    
    #include <easydb>
    #include <izcmd>
    #include <sscanf2>
    

  • DECLARING VARIABLES & MACROS
    Second step will be defining general settings like password length:

    Code:

    #define MIN_PASSWORD_LENGTH (5)
    #define MAX_PASSWORD_LENGTH (45)
    

    And maximum login attempts a user can get: (i am setting it 3 | note this should not be below 1)

    Code:

    #define MAX_LOGIN_ATTEMPTS (3)
    

    Declare a player information holder enumerator:

    Code:

    enum E_ACCOUNT
    {
            E_ACCOUNT_PASSWORD[MAX_PASSWORD_LENGTH],
            E_ACCOUNT_SALT[MAX_PASSWORD_LENGTH],
            E_ACCOUNT_KILLS,
            E_ACCOUNT_DEATHS,
            E_ACCOUNT_MONEY,
            E_ACCOUNT_SCORE
    };
    new p_Account[MAX_PLAYERS][E_ACCOUNT];
    

    And another variable to store player's total login attempts:

    Code:

    new p_LoginAttempts[MAX_PLAYERS];
    

  • INITIALIZING DATABASE
    If you are using filterscript:

    Code:

    public OnFilterScriptInit()
    {
    }
    

    Or gamemode users will use this callback:

    Code:

    public OnGameModeInit()
    {
    }
    

    I'll be using gamemode as an example for now.

    For MySQL users:

    Code:

    public OnGameModeInit()
    {
            DB::Init("database name", "server/host name", "username", "password");
            
            // Creating/Verifying table where we'll save user data
            DB::VerifyTable("Users", "ID", false,
                                                    "Name", STRING,
                                                    "Password", STRING,
                                                    "Salt", STRING,
                                                    "IP", STRING,
                                                    "Kills", INTEGER,
                                                    "Deaths", INTEGER,
                                                    "Money", INTEGER,
                                                    "Score", INTEGER);
    
            return 1;
    }
    

    For SQLite users:

    Code:

    public OnGameModeInit()
    {
            DB::Init("database name", "server/host name", "username", "password");
            
            // Creating/Verifying table where we'll save user data
            DB::VerifyTable("Users", "ID", false,
                                                    "Name", STRING,
                                                    "Password", STRING,
                                                    "Salt", STRING,
                                                    "IP", STRING,
                                                    "Kills", INTEGER,
                                                    "Deaths", INTEGER,
                                                    "Money", INTEGER,
                                                    "Score", INTEGER);
    
            return 1;
    }
    

    NOTE: If you want to add more columns, you can simply edit the VerifyTable function in your script. For example my table "Users" is already existing with all the given rows below. But now i want to create another column/field called "Health" as FLOAT (say), so i can very easily do that with the following edition:

    Code:

    DB::VerifyTable("Users", "ID", false,
                                                    "Name", STRING,
                                                    "Password", STRING,
                                                    "Salt", STRING,
                                                    "IP", STRING,
                                                    "Kills", INTEGER,
                                                    "Deaths", INTEGER,
                                                    "Money", INTEGER,
                                                    "Score", INTEGER,
                                                    "Health", FLOAT);
    
  • SHOWING LOGIN/REGISTER DIALOGS
    Player account existence is a only checked by the name's registration. So here we do the same, we check if the user's name is already existing in the database table "Users", if yes show login dialog otherwise a register dialog.

    Code:

    public OnPlayerConnect(playerid)
    {
        p_LoginAttempts[playerid] = 0; // reset login attempts to 0
    
            new name[MAX_PLAYER_NAME];
            GetPlayerName(playerid, name, MAX_PLAYER_NAME);
            
            DB::Fetch("Users", 0, _, _, _, "`Name` = '%q'", name);
            if (fetch_rows_count() > 0) //  if the user is existing member (LOGIN)
            {
                fetch_string("Password", p_Account[playerid][E_ACCOUNT_PASSWORD], MAX_PASSWORD_LENGTH);
                fetch_string("Salt", p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH);
                p_Account[playerid][E_ACCOUNT_KILLS] = fetch_int("Kills");
                p_Account[playerid][E_ACCOUNT_DEATHS] = fetch_int("Deaths");
                p_Account[playerid][E_ACCOUNT_MONEY] = fetch_int("Money");
                p_Account[playerid][E_ACCOUNT_SCORE] = fetch_int("Score");
                
                Dialog_Show(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login account...", "Good to see you back to SA-MP 0.3.7 Server! Please complete the login formality to access your account (password required).", "Login", "Quit");
            }
            else // user is new (REGISTER)
            {
                p_Account[playerid][E_ACCOUNT_PASSWORD][0] = EOS;
                p_Account[playerid][E_ACCOUNT_SALT][0] = EOS;
                p_Account[playerid][E_ACCOUNT_KILLS] = 0;
                p_Account[playerid][E_ACCOUNT_DEATHS] = 0;
                p_Account[playerid][E_ACCOUNT_MONEY] = 0;
                p_Account[playerid][E_ACCOUNT_SCORE] = 0;
    
                Dialog_Show(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Register account...", "Welcome to SA-MP 0.3.7 Server! Please complete this small registeration formality to sign-up with us (password required).", "Register", "Quit");
            }
            
            fetcher_close();
            return 1;
    }
    

    Now we'll handle the dialog's responses...

    <easydialog> handles dialog responses just like <zcmd>, so this piece of code shall be placed independently maybe under OnPlayerConnect callback or whereever you wish.

    DIALOG_LOGIN

    Code:

    Dialog:DIALOG_LOGIN(playerid, response, listitem, inputtext[])
    {
            // if player clicks "Quit" (will be kicked)
            if (!response)
                return Kick(playerid);
    
            // we'll hash player password here which will be further used for comparing
            new password[MAX_PASSWORD_LENGTH];
            SHA256_PassHash(inputtext, p_Account[playerid][E_ACCOUNT_SALT], password, MAX_PASSWORD_LENGTH);
    
            // as we have stored player data when he/she connected, so we'll check the password is matching
            if (strcmp(p_Account[playerid][E_ACCOUNT_PASSWORD], password)) // if password is incorrect
            {
                p_LoginAttempts[playerid]++;
                
                new str[150];
                format(str, sizeof (str), "Incorrect password, you are left with %i/%i attempts.", p_LoginAttempts[playerid], MAX_LOGIN_ATTEMPTS);
                SendClientMessage(playerid, 0xFFFFFFFF, str);
                
                if (p_LoginAttempts[playerid] == MAX_LOGIN_ATTEMPTS)
                            return Kick(playerid);
    
                return Dialog_Show(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login account...", "Good to see you back to SA-MP 0.3.7 Server! Please complete the login formality to access your account (password required).", "Login", "Quit");
            }
    
            // player has successfully logged-in
            new name[MAX_PLAYER_NAME];
            GetPlayerName(playerid, name, MAX_PLAYER_NAME);
    
            new str[150];
            format(str, sizeof (str), "Welcome back %s [id: %i]...", name, playerid);
            SendClientMessage(playerid, 0xFFFFFFFF, str);
            
            ResetPlayerMoney(playerid);
            GivePlayerMoney(playerid, p_Account[playerid][E_ACCOUNT_MONEY]);
            SetPlayerScore(playerid, p_Account[playerid][E_ACCOUNT_SCORE]);
            
            // update player information
            new ip[18];
            GetPlayerIp(playerid, ip, sizeof (ip));
            
            DB::Update("Users", p_Account[playerid][E_ACCOUNT_SQLID], 1,
                            "IP", STRING, ip);
    
            return 1;
    }
    

    DIALOG_REGISTER
    Registration covers password hashing part. What's unique about this is that every new user gets a separate password salt for their account, so in case a hacker gets the salt of one account, other accounts won't be vulnerable to being hacked as well **

    Code:

    Dialog:DIALOG_REGISTER(playerid, response, listitem, inputtext[])
    {
            // if player clicks "Quit" (will be kicked)
            if (!response)
                return Kick(playerid);
    
            // check if password is not empty
            if (!inputtext[0] || inputtext[0] == ' ')
            {
                SendClientMessage(playerid, 0xFFFFFFFF, "Invalid password length, cannot be empty.");
                
                return Dialog_Show(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Register account...", "Welcome to SA-MP 0.3.7 Server! Please complete this small registeration formality to sign-up with us (password required).", "Register", "Quit");
            }
    
            // check for password lengths
            new len = strlen(inputtext);
            if (len > MAX_PASSWORD_LENGTH || len < MIN_PASSWORD_LENGTH)
            {
                new str[150];
                format(str, sizeof (str), "Invalid password length, must be between %i - %i chars.", MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH);
                SendClientMessage(playerid, 0xFFFFFFFF, str);
    
                return Dialog_Show(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Register account...", "Welcome to SA-MP 0.3.7 Server! Please complete this small registeration formality to sign-up with us (password required).", "Register", "Quit");
            }
    
            // now we'll create a random password salt (much security eh!)
            #define randomex(%0,%1) \
                    (random(%1-(%0))+%0)
            
            for (new i; i < MAX_PASSWORD_LENGTH; i++)
            {
                switch (random(4))
                {
                    case 0:
                        format(p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH, "%s%c", p_Account[playerid][E_ACCOUNT_SALT], randomex(65, 90));
    
                    case 1:
                        format(p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH, "%s%c", p_Account[playerid][E_ACCOUNT_SALT], randomex(97, 122));
    
                    case 2:
                        format(p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH, "%s%c", p_Account[playerid][E_ACCOUNT_SALT], randomex(33, 63));
    
                    case 3:
                        format(p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH, "%s%c", p_Account[playerid][E_ACCOUNT_SALT], randomex(128, 155));
                }
            }
    
            // hashing the password
            SHA256_PassHash(inputtext, p_Account[playerid][E_ACCOUNT_SALT], p_Account[playerid][E_ACCOUNT_PASSWORD], MAX_PASSWORD_LENGTH);
    
            // player has successfully registered
            new name[MAX_PLAYER_NAME];
            GetPlayerName(playerid, name, MAX_PLAYER_NAME);
    
            new str[150];
            format(str, sizeof (str), "Welcome %s [id: %i]...", name, playerid);
            SendClientMessage(playerid, 0xFFFFFFFF, str);
    
            // create player's row into "Users" table
            new ip[18];
            GetPlayerIp(playerid, ip, sizeof (ip));
    
            DB::CreateRow("Users",
                            "Name", name,
                            "Password", p_Account[playerid][E_ACCOUNT_PASSWORD],
                                            "Salt", p_Account[playerid][E_ACCOUNT_SALT],
                            "IP", ip);
    
            return 1;
    }
    

  • INCREASING KILLS & DEATHS

    Code:

    public OnPlayerDeath(playerid, killerid, reason)
    {
        p_Account[playerid][E_ACCOUNT_DEATHS]++; // increase player's deaths count
        
            if (killerid != INVALID_PLAYER_ID)
            p_Account[killerid][E_ACCOUNT_KILLS]++; // increase killer's kills count if the id is of a connected player
    
            return 1;
    }
    

  • SAVING PLAYER DATA
    I am only saving player's data when they disconnect/leave the server. Because this callback is called every time no matter if player's PC shuts down or whatever...

    Code:

    public OnPlayerDisconnect(playerid, reason)
    {
            DB::Update("Users", p_Account[playerid][E_ACCOUNT_SQLID], 1,
                            "Kills", INTEGER, p_Account[playerid][E_ACCOUNT_KILLS],
                            "Deaths", INTEGER, p_Account[playerid][E_ACCOUNT_DEATHS],
                            "Score", INTEGER, GetPlayerScore(playerid),
                            "Money", INTEGER, GetPlayerMoney(playerid));
            return 1;
    }
    

  • EXITING EASYDB
    EasyDB has to be closed when your server is closed or most importantly in case of filterscripts when they are unloaded:

    Code:

    // for filterscripts
    public OnFilterScriptExit()
    {
            DB::Exit();
            return 1;
    }
    
    // if using gamemode
    public OnGameModeExit()
    {
            DB::Exit();
            return 1;
    }
    

Finish (overall code)
MySQL version.
You know what to do if you want to use SQLite (if not, read the second step in "Scripting" section)

Code:

#include <a_samp>

#define CONNECTION_TYPE_MYSQL
#define ENABLE_CONSOLE_MESSAGES // tells "easydb" that we would need console messages/debugs while run time to keep checks and detect if any errors/warnings

#include <easydb>
#include <izcmd>
#include <easydialog>
#include <sscanf2>

#define MIN_PASSWORD_LENGTH (5)
#define MAX_PASSWORD_LENGTH (45)

#define MAX_LOGIN_ATTEMPTS (3)

enum E_ACCOUNT
{
        E_ACCOUNT_SQLID,
        E_ACCOUNT_PASSWORD[MAX_PASSWORD_LENGTH],
        E_ACCOUNT_SALT[MAX_PASSWORD_LENGTH],
        E_ACCOUNT_KILLS,
        E_ACCOUNT_DEATHS,
        E_ACCOUNT_MONEY,
        E_ACCOUNT_SCORE
};
new p_Account[MAX_PLAYERS][E_ACCOUNT];

new p_LoginAttempts[MAX_PLAYERS];

public OnGameModeInit()
{
        // Now if you are using MySQL
        DB::Init("database name", "server/host name", "username", "password");

        // or SQLite users
        DB::Init("database name");

        // Creating/Verifying table where we'll save user data
        DB::VerifyTable("Users", "ID", false,
                                                "Name", STRING,
                                                "Password", STRING,
                                                "Salt", STRING,
                                                "IP", STRING,
                                                "Kills", INTEGER,
                                                "Deaths", INTEGER,
                                                "Money", INTEGER,
                                                "Score", INTEGER);
        return 1;
}

public OnPlayerConnect(playerid)
{
    p_LoginAttempts[playerid] = 0; // reset login attempts to 0

        new name[MAX_PLAYER_NAME];
        GetPlayerName(playerid, name, MAX_PLAYER_NAME);
        
        DB::Fetch("Users", 0, _, _, _, "`Name` = '%q'", name);
        if (fetch_rows_count() > 0) //  if the user is existing member (LOGIN)
        {
            p_Account[playerid][E_ACCOUNT_SQLID] = fetch_row_id();
            fetch_string("Password", p_Account[playerid][E_ACCOUNT_PASSWORD], MAX_PASSWORD_LENGTH);
            fetch_string("Salt", p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH);
            p_Account[playerid][E_ACCOUNT_KILLS] = fetch_int("Kills");
            p_Account[playerid][E_ACCOUNT_DEATHS] = fetch_int("Deaths");
            p_Account[playerid][E_ACCOUNT_MONEY] = fetch_int("Money");
            p_Account[playerid][E_ACCOUNT_SCORE] = fetch_int("Score");
            
            Dialog_Show(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login account...", "Good to see you back to SA-MP 0.3.7 Server! Please complete the login formality to access your account (password required).", "Login", "Quit");
        }
        else // user is new (REGISTER)
        {
            p_Account[playerid][E_ACCOUNT_SQLID] = -1;
            p_Account[playerid][E_ACCOUNT_PASSWORD][0] = EOS;
            p_Account[playerid][E_ACCOUNT_SALT][0] = EOS;
            p_Account[playerid][E_ACCOUNT_KILLS] = 0;
            p_Account[playerid][E_ACCOUNT_DEATHS] = 0;
            p_Account[playerid][E_ACCOUNT_MONEY] = 0;
            p_Account[playerid][E_ACCOUNT_SCORE] = 0;

            Dialog_Show(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Register account...", "Welcome to SA-MP 0.3.7 Server! Please complete this small registeration formality to sign-up with us (password required).", "Register", "Quit");
        }
        
        fetcher_close();
        return 1;
}

Dialog:DIALOG_LOGIN(playerid, response, listitem, inputtext[])
{
        // if player clicks "Quit" (will be kicked)
        if (!response)
            return Kick(playerid);

        // we'll hash player password here which will be further used for comparing
        new password[MAX_PASSWORD_LENGTH];
        SHA256_PassHash(inputtext, p_Account[playerid][E_ACCOUNT_SALT], password, MAX_PASSWORD_LENGTH);

        // as we have stored player data when he/she connected, so we'll check the password is matching
        if (strcmp(p_Account[playerid][E_ACCOUNT_PASSWORD], password)) // if password is incorrect
        {
            p_LoginAttempts[playerid]++;
            
            new str[150];
            format(str, sizeof (str), "Incorrect password, you are left with %i/%i attempts.", p_LoginAttempts[playerid], MAX_LOGIN_ATTEMPTS);
            SendClientMessage(playerid, 0xFFFFFFFF, str);
            
            if (p_LoginAttempts[playerid] == MAX_LOGIN_ATTEMPTS)
                        return Kick(playerid);

            return Dialog_Show(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login account...", "Good to see you back to SA-MP 0.3.7 Server! Please complete the login formality to access your account (password required).", "Login", "Quit");
        }

        // player has successfully logged-in
        new name[MAX_PLAYER_NAME];
        GetPlayerName(playerid, name, MAX_PLAYER_NAME);

        new str[150];
        format(str, sizeof (str), "Welcome back %s [id: %i]...", name, playerid);
        SendClientMessage(playerid, 0xFFFFFFFF, str);
        
        ResetPlayerMoney(playerid);
        GivePlayerMoney(playerid, p_Account[playerid][E_ACCOUNT_MONEY]);
        SetPlayerScore(playerid, p_Account[playerid][E_ACCOUNT_SCORE]);
        
        // update player information
        new ip[18];
        GetPlayerIp(playerid, ip, sizeof (ip));
        
        DB::Update("Users", p_Account[playerid][E_ACCOUNT_SQLID], 1,
                        "IP", STRING, ip);

        return 1;
}

Dialog:DIALOG_REGISTER(playerid, response, listitem, inputtext[])
{
        // if player clicks "Quit" (will be kicked)
        if (!response)
            return Kick(playerid);

        // check if password is not empty
        if (!inputtext[0] || inputtext[0] == ' ')
        {
            SendClientMessage(playerid, 0xFFFFFFFF, "Invalid password length, cannot be empty.");
            
            return Dialog_Show(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Register account...", "Welcome to SA-MP 0.3.7 Server! Please complete this small registeration formality to sign-up with us (password required).", "Register", "Quit");
        }

        // check for password lengths
        new len = strlen(inputtext);
        if (len > MAX_PASSWORD_LENGTH || len < MIN_PASSWORD_LENGTH)
        {
            new str[150];
            format(str, sizeof (str), "Invalid password length, must be between %i - %i chars.", MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH);
            SendClientMessage(playerid, 0xFFFFFFFF, str);

            return Dialog_Show(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Register account...", "Welcome to SA-MP 0.3.7 Server! Please complete this small registeration formality to sign-up with us (password required).", "Register", "Quit");
        }

        // Now we'll create a random password salt (much security eh!)
        #define randomex(%0,%1) \
                (random(%1-(%0))+%0)
        
        for (new i; i < MAX_PASSWORD_LENGTH; i++)
        {
            switch (random(4))
            {
                case 0:
                    format(p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH, "%s%c", p_Account[playerid][E_ACCOUNT_SALT], randomex(65, 90));

                case 1:
                    format(p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH, "%s%c", p_Account[playerid][E_ACCOUNT_SALT], randomex(97, 122));

                case 2:
                    format(p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH, "%s%c", p_Account[playerid][E_ACCOUNT_SALT], randomex(33, 63));

                case 3:
                    format(p_Account[playerid][E_ACCOUNT_SALT], MAX_PASSWORD_LENGTH, "%s%c", p_Account[playerid][E_ACCOUNT_SALT], randomex(128, 155));
            }
        }

        // hashing the password
        SHA256_PassHash(inputtext, p_Account[playerid][E_ACCOUNT_SALT], p_Account[playerid][E_ACCOUNT_PASSWORD], MAX_PASSWORD_LENGTH);

        // player has successfully registered
        new name[MAX_PLAYER_NAME];
        GetPlayerName(playerid, name, MAX_PLAYER_NAME);

        new str[150];
        format(str, sizeof (str), "Welcome %s [id: %i]...", name, playerid);
        SendClientMessage(playerid, 0xFFFFFFFF, str);

        // create player's row into "Users" table
        new ip[18];
        GetPlayerIp(playerid, ip, sizeof (ip));

        DB::CreateRow("Users",
                        "Name", STRING, name,
                        "Password", STRING, p_Account[playerid][E_ACCOUNT_PASSWORD],
                                        "Salt", STRING, p_Account[playerid][E_ACCOUNT_SALT],
                        "IP", STRING, ip);

        return 1;
}

public OnPlayerDeath(playerid, killerid, reason)
{
    p_Account[playerid][E_ACCOUNT_DEATHS]++; // increase player's deaths count
    
        if (killerid != INVALID_PLAYER_ID)
        p_Account[killerid][E_ACCOUNT_KILLS]++; // increase killer's kills count if the id is of a connected player

        return 1;
}

public OnPlayerDisconnect(playerid, reason)
{
        DB::Update("Users", p_Account[playerid][E_ACCOUNT_SQLID], 1,
                        "Kills", INTEGER, p_Account[playerid][E_ACCOUNT_KILLS],
                        "Deaths", INTEGER, p_Account[playerid][E_ACCOUNT_DEATHS],
                        "Score", INTEGER, GetPlayerScore(playerid),
                        "Money", INTEGER, GetPlayerMoney(playerid));
        return 1;
}

public OnGameModeExit()
{
        DB::Exit();
        return 1;
}
__________________
Offering paid scripting service
Wanna work on a third person shooter multiplayer game? Greater graphics, good AI., decent amount of maps with good details and a great todo list. Contact me on Skype: facebook:agneesesaini.

☠ Neufoxplays ☠

Aucun commentaire:

Enregistrer un commentaire