[Tutorial] How to use Y_INI
#1

Introduction
I know what you're thinking - "ANOTHER Y_INI tutorial?!", and yes, it has to do with Y_INI, but I promise that this tutorial is a bit different from the other ones that you have seen. I've viewed them myself before creating this tutorial, and have concluded that they all make Y_INI seem like a foreign language - it's really easy once you get the hang of it!
What do I need?
In order to use Y_INI, you're going to need to download YSI. You can do so by going to the YSI topic and following the instructions there. It's a very simple installation, and it gives you access to a whole plethora of new features.
Creating a User System (Register/Login)
Creating our Dialog Enum
This simple nameless enum will assign a number value to each new dialog which is entered. This helps to prevent dialog ID mismatches that come from definitions! No more changing dialog ids around to make sure they don't conflict - this automatically makes sure they don't conflict.
pawn Code:
enum
{
    DiagRegister, // Our Register Dialog (ID 0)
    DiagLogin // Our Login Dialog (ID 1)
}
Creating our User Variables
First off, we will create our account enum, this stores all of our account variables! This is what is saved/loaded in this tutorial.
pawn Code:
enum AccountVariables
{
    Password [129], // a string with a length of 129 cells.
    Admin, // a simple integer used to store our Administrator value
   
    Float: LastX, // a float used to store the last known X coordinate
    Float: LastY, // a float used to store the last known Y coordinate
    Float: LastZ, // a float used to store the last known Z coordinate
    Float: LastA // a float used to store the last known angle (which is also a coordinate)
}
Next, we will create our PlayerInfo array along with a simple bool (true/false) variable which lets us detect if someone has logged in or not, but they don't do anything yet, we're simply creating them right now!
pawn Code:
new
    PlayerInfo [ MAX_PLAYERS ] [ AccountVariables ],
    bool: PlayerLogged [ MAX_PLAYERS ] = false;
Useful Definitions
These are a couple definitions that I personally use to make things easier - you can choose to do things the hard way, or you can spend a couple minutes creating something that will save you time in the long run.
pawn Code:
#define ACCOUNT_PATH        "Players/%s.ini"
#define ShowLoginDialog     ShowPlayerDialog (playerid, DiagLogin, DIALOG_STYLE_PASSWORD, "Welcome back!", "Please enter your account password to continue.", "OK", "Quit")
#define ShowRegisterDialog  ShowPlayerDialog (playerid, DiagRegister, DIALOG_STYLE_INPUT, "Welcome to My Server!", "Please enter a password for your account.", "OK", "Quit")
Useful Functions
While you don't necessarily NEED this bit of code, it definitely helps to have it! This takes our "ACCOUNT_PATH" definition and inserts their name, allowing us to figure out where their file will be located.
pawn Code:
GetPlayerPath (playerid)
{
    new
        PlayerPath [64],
        PlayerName [MAX_PLAYER_NAME];
       
    GetPlayerName (playerid, PlayerName, MAX_PLAYER_NAME);
    format (PlayerPath, sizeof PlayerPath, ACCOUNT_PATH, PlayerName);  
       
    return PlayerPath;
}
Part 1: What happens when a player connects?
Well, what do you WANT to happen when a player connects? For this tutorial, we will be showing them a register or a login dialog (depending if their file exists or not) and asking them to authenticate their account. Depending if they have to register or login, this will either create their file and load their information, or just simply load their information.
pawn Code:
public OnPlayerConnect(playerid)
{
    if(fexist(GetPlayerPath(playerid)))
    {
        INI_ParseFile (GetPlayerPath (playerid), "LoadAccount_%s", .bExtra = true, .extra = playerid);
        /* INI_ParseFile - it may seem VERY complicated, but it's really pretty simple!
        The parameters of it are like so:
            INI_ParseFile(PATH, FUNCTION, SEND EXTRA DATA?, EXTRA DATA);
        Keeping that in mind, we are loading the file at "Players/(playername).ini" through
        the callback "LoadAccount" and sending our playerid as extra data. */


        ShowLoginDialog;
    }
    else
    {
        ShowRegisterDialog;
        /* You can only use "ShowRegisterDialog;" if you used my "useful" definitions above, otherwise
        just show them the "DiagRegister" dialog. */

    }
    return 1;
}
Now that we have shown them the dialog, what next? You'll quickly find out that the dialog you just showed them does absolutely nothing! Why is this? This is because you have not put any code under OnDialogResponse! OnDialogResponse handles everything that happens inside of a dialog - from pressing the ESC key, to pressing buttons.
Part 2: What happens when a player disconnects?
Now, we're going to save all of their information (except their password) every time they disconnect.

pawn Code:
public OnPlayerDisconnect(playerid, reason)
{
    if (PlayerLogged [playerid] == true) // Make sure they're logged in first!
    {
        new
            INI: Player = INI_Open ( GetPlayerPath (playerid) ), // Open their file!
            Float: CurX,
            Float: CurY,
            Float: CurZ,
            Float: CurA;
           
        GetPlayerPos (playerid, CurX, CurY, CurZ);
        GetPlayerFacingAngle (playerid, CurA);
           
        INI_SetTag (Player, "data");
        // Make sure every time you write anything to their file, you set the tag!
       
        INI_WriteInt (Player, "Admin", PlayerInfo [playerid] [Admin]);
       
        INI_WriteFloat (Player, "LastX", CurX); // Write their X coordinate
        INI_WriteFloat (Player, "LastY", CurY); // Write their Y coordinate
        INI_WriteFloat (Player, "LastZ", CurZ); // Write their Z coordinate
        INI_WriteFloat (Player, "LastA", CurA); // Write their Angle coordinate
       
        INI_Close (Player); // Close the file!
       
        PlayerLogged [playerid] = false; // Set them as logged out!
    }
    return 1;
}
Part 3: Making the Register dialog work correctly
I will explain everything that is inside of the callback (OnDialogResponse) inside of the code itself, so please read along with the green comments next to the code and DO NOT simply copy & paste the code into your script, this does not help you learn, and it ends up hurting you more than it helps you.
pawn Code:
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
    switch (dialogid) // This is our switch statement, it allows us to go through our dialogid's.
    {
        case DiagRegister: // This allows us to handle what happens inside of the Register dialog.
        {
            if (response) // Any code inside of here will be executed if they press the first button.
            {
                new
                    INI: Player = INI_Open ( GetPlayerPath (playerid) );
                    /* This creates our INI file ("Player") and opens the file that is located at
                    "Players/(playername).ini", this could be different for you, depending on if
                    you edited the "ACCOUNT_PATH" definition. */

                       
                INI_SetTag (Player, "data");
                /* The thing that makes Y_INI so weird to people is the use of tags - this allows
                us to separate our INI files into sections, in this tutorial we will only be using
                one tag - "data", but remember that for later! */

                   
                INI_WriteString (Player, "Password", inputtext);
                /* This stores whatever we wrote in our dialog into the "Password" variable inside of
                our enum. I also HIGHLY recommend hashing your passwords using Whirlpool! */


                INI_WriteInt (Player, "Admin", 0);
                // This stores the value of zero into the "Admin" variable inside of our enum.
                   
                INI_Close (Player);
                // This closes our file ("Player") - ALWAYS remember to do this!
                   
                INI_ParseFile (GetPlayerPath (playerid), "LoadAccount_%s", .bExtra = true, .extra = playerid);
                /* INI_ParseFile - it may seem VERY complicated, but it's really pretty simple!
                The parameters of it are like so:
                            INI_ParseFile(PATH, FUNCTION, SEND EXTRA DATA?, EXTRA DATA);
                Keeping that in mind, we are loading the file at "Players/(playername).ini" through
                the callback "LoadAccount" and sending our playerid as extra data. */


                PlayerLogged [playerid] = true;
                // Now that their file is created and loaded, we can set them as logged in!
           
                SetSpawnInfo (playerid, NO_TEAM, 119, 0.0, 0.0, 5.0, 0.0, 0,0, 0,0, 0,0);
                SpawnPlayer (playerid);
                // Simple spawn code for testing purposes.
            }
            else // Any code inside of here will be executed if they press the second button or ESC key.
            {
                SendClientMessage (playerid, -1, "Sorry to hear that! Hopefully we meet again.");
                Kick (playerid);
                // Send them a message and kick them if they click "Quit"
            }
        }
    }
    return 1;
}
Part 4: Making the Login dialog work correctly
Inside of our switch statement, under the DiagRegister case, we're going to add our DiagLogin case! Once again, I will explain everything in comments in the code.

pawn Code:
case DiagLogin:
{
    if (response) // They clicked the first button!
    {
        if( isnull (inputtext) ) // Assuming you have sscanf, this is already included!
        {
            /* This is NOT optional, without it if you just press the button on the dialog
            it will automatically log you in, without any password. */

            ShowLoginDialog;
            return 0;
        }
               
        if ( strcmp (inputtext, PlayerInfo [playerid] [Password], true) == 0 )
        { // This checks if the password they entered on the dialog is the same as their file.
            PlayerLogged [playerid] = true; // Set them as logged in!

            // Spawn them at their last known position!
            SetSpawnInfo (playerid, NO_TEAM, 119, PlayerInfo [playerid] [LastX], PlayerInfo [playerid] [LastY], PlayerInfo [playerid] [LastZ], \
                PlayerInfo [playerid] [LastA], 0,0, 0,0, 0,0
            );
            SpawnPlayer (playerid);
        }
        else // What happens if their password is wrong?
        {
            SendClientMessage (playerid, -1, "Incorrect password.");
            ShowLoginDialog;
        }
    }
    else // Any code inside of here will be executed if they press the second button or ESC key.
    {
        SendClientMessage (playerid, -1, "Sorry to hear that! Hopefully we meet again.");
        Kick (playerid);
        // Send them a message and kick them if they click "Quit"
    }
}
Part 5: Creating the "LoadAccount" function
If you have tried your code up til now, you will notice that no loading functions work. If you're curious as to why, wrap your mind around this - in our INI_ParseFile function, we transfer the data to a "LoadAccount" function - did we create that function yet? No we have not, and THAT is why loading is non functional as of right now. Let's get to it, shall we?

pawn Code:
forward LoadAccount_data (playerid, name[], value[]);
public LoadAccount_data (playerid, name[], value[])
{
    // You'll notice that the actual function name is "LoadAccount_data",
    // "data" is the tag we used above, therefore it'll load correctly.

    // "playerid" is the value we passed from our INI_ParseFile function.
    // "name" is the value we are reading ("Password", "Admin", ect)
    // "value" is the value of the "name" we are currently reading

    INI_String ("Password", PlayerInfo [playerid] [Password], 129); // Loads "Password" into our PlayerInfo array.
    INI_Int ("Admin", PlayerInfo [playerid] [Admin]); // Loads "Admin" into our PlayerInfo array.
   
    INI_Float ("LastX", PlayerInfo [playerid] [LastX]); // Loads "LastX" into our PlayerInfo array.
    INI_Float ("LastY", PlayerInfo [playerid] [LastY]); // Loads "LastY" into our PlayerInfo array.
    INI_Float ("LastZ", PlayerInfo [playerid] [LastZ]); // Loads "LastZ" into our PlayerInfo array.
    INI_Float ("LastA", PlayerInfo [playerid] [LastA]); // Loads "LastA" into our PlayerInfo array.
    return 1;
}
Part 6: Just kidding, there is no part 6
Congratulations! You have completed your first Y_INI register/login system, how does it feel? You can only get better from here, and I hope everything was explained well enough to allow you to learn. If there is anything I can better explain, then please reply and let me know. With all of that being said, take a breath of relief that this HUGE tutorial is done, and you have a fully functioning user system!
Reply
#2

Great tutorial, definitely is different from the rest. I like how you explained everything. The other ones were a bit hard to understand.
Reply
#3

Quote:
Originally Posted by AaronKillz
View Post
Great tutorial, definitely is different from the rest. I like how you explained everything. The other ones were a bit hard to understand.
Glad you like it, I'll add another tutorial inside of this one on how to create dynamic systems with Y_INI, as that seems to be a foreign topic with it too!
Reply
#4

Great tutorial Sky. Good to see you're still doing pawn.
Reply
#5

Quote:
Originally Posted by SchurmanCQC
View Post
Great tutorial Sky. Good to see you're still doing pawn.
Thanks, and you of all people should know you never fully leave!
Reply
#6

Nice job on this!
Reply
#7

Nice Tutorial.
rep+
Reply
#8

Glad to hear it's well received.
Reply
#9

Really thanks for this tutorial
Reply
#10

Not a problem, will be adding more to it in the future.
Reply
#11

Great tut dude, definitely got me with the Ini_parsefile
Reply
#12

Quote:
Originally Posted by Calvingreen17
Посмотреть сообщение
Great tut dude, definitely got me with the Ini_parsefile
Glad to hear I could clear up some confusion with you!
Reply
#13

Nice one +rep.
Reply
#14

Good tutorial, good work.
Reply
#15

Glad it's shedding some light on the topic. Will be writing up the dynamic function tutorial sometime this week or next, not entirely sure yet.
Reply


Forum Jump:


Users browsing this thread: 3 Guest(s)