19.05.2015, 20:48
(
Последний раз редактировалось Overhaul; 31.01.2018 в 10:11.
)
Explanation for this post
This is officially my first tutorial on this forum. I won't go through my background, instead I'd like to give some explanation on why I am posting this tutorial. First of all, newbienoob's tutorial does the job well. It gives users an idea on how MySQL within PAWN works (specifically registering and logging in). The thing that bothers me and probably a few other members is that his tutorial is not being maintained right now (he left SA:MP) because of that the tutorial could lack on important updates. Let us get straight to the tutorial.
TL ; DR: It could lack behind on important and essential updates of the plugins/includes that it uses.
Necessities
- The latest version of the MySQL plugin (GitHub).
- The latest version of the Whirlpool plugin (Forum topic).
- The latest version of XAMPP (Apache friends/XAMPP).
It is also recommended to check on the WIKI-page of the MySQL plugin for additional information regarding MySQL functions within the plugin: https://sampwiki.blast.hk/wiki/MySQL/R33.
Table of content
I will not be explaining what XAMPP exactly is, you can read all about it on their website. One thing you should know is that you can only run XAMPP through xampp-control.exe.
Extra note: pictures used in this tutorial are displayed as a thumbnail. Click on the picture to zoom in to it.
When launching XAMPP through xampp-control.exe, you should be able to see the following window:
For this tutorial and for SA:MP, we only need two modules: Apache and MySQL. Feel free to check what the other modules do for the sake of knowledge but we don't need them.
To initialize the Apache and the MySQL modules simply press the 'start' button under the 'actions' sub-title.
Note that Apache listens on port 80 and 443. A known application to interfere with this is Skype. I will show you how to run Skype and Apache all together in the FAQ section.
Great! The next step is creating your database and the required tables. To do so, simply click 'Admin' for the MySQL module.
Your standard browser should redirect you to the phpmyadmin page of your MySQL server (localhost/phpmyadmin).
Of course we don't need to go through all these options. We only need to create one database for this tutorial. Again, you can go through all the options for the sake of knowledge. There are two methods - easily visible - one being the 'new' button in the left sidebar and the second one being in the 'databases' tab. The 'new' button redirects to the 'databases' tab anyway so it doesn't matter what you choose.
You have to fill in the name of your database and click create when you did so. This will add your database to the left sidebar in between the rest of the databases. For the sake of the tutorial, we will call our database 'myserver'.
Fantastic, you have successful created your own MySQL database! We are not finished yet, though. The next step is create individual tables in our database to organize our data (Data for Players, vehicles, houses, jobs, weapons, ...). This can be done by clicking on your database in the left sidebar.
You will soon see that it asks you to create tables with a certain amount of columns. For the sake of this tutorial, we create a table called 'accounts' and it will store the account data of the player.
For integers use the data value type (INT).
For strings use the data value type (VARCHAR).
For floats use the data value type (FLOAT).
The basic register/login script needs a minimum of 10 columns (+ 1 for the VIP rank). The database ID for the player (which we will use to link other tables with this user), the player's name, the player's password, the player's IP, The player's administrator rank, the player's VIP rank, the player's money and the player's position (X, Y, Z, A). Go ahead and add the remaining columns to your table (till you have 11 columns).
Notice the index set to 'PRIMARY' and the A_I option checked. Basically we will use the ID column to identify the player in the database and for every new record, the ID goes up by one. For example: I made the first account on your server, my database ID is now 1. You make your account after I do, you ID will be 2. Click 'save' when you are ready to move on to the scripting part of this tutorial.
Congratulations! Your database has been created and configured successfully and now it is time to make it of use.
Scripting
We need two basic includes to make a server with a MySQL plugin. The a_samp include and of course the a_mysql include. You can start off by including both.
Our second step is filling in the configuration settings of our MySQL database so that our SA:MP server can connect to the MySQL database. We do this with the help of defines - This makes editing the settings later on very easy!
MYSQL_HOST is the IP to our MySQL database.
MYSQL_USER is the user of our MySQL database.
MYSQL_DATABASE is the name of the MySQL database that we created in the beginning of this tutorial.
MYSQL_PASSWORD is the password to authorize the connection. XAMPP has no default password to we can leave this blank.
The defines given above resemble the starting coordinates of new players who just registered. These are the first coordinates inserted in the database upon registration.
SPAWN_X is the X-coordinate (East and West) in the GTA world.
SPAWN_Y is the Y-coordinate (North and South) in the GTA world.
SPAWN_Z is the Z-coordinate (Up and down) in the GTA world.
SPAWN_A is the A-coodinate (or the facing angle) in the GTA world.
To get your in game coordinates use /save. SA:MP saves the coordinates in a file under 'My Documents/GTA San Andreas User Files/SAMP'
For the register/login dialogues, I decided to not use defines but an enum. This method avoids dialogue ID collisions, meaning that you don't have to assign an ID to every dialogue you make.
Of course we have to make sure that the script recognizes our database. For that, we have to create a variable that will hold our connection data. This variable is global.
Hashing passwords is a must. It is even illegal to not hash passwords - safety of all internet users must be enforced at all times! To secure the passwords of our users, we use a hashing method called 'Whirlpool'. It is best to add this native under the includes.
Next up is our enum for the player's data. I expect that you know what an enum is and how to use it. You can customize the enum yourself; add date, change names of enumerators, etc... .
You should recognize these enumerators from earlier. These are also the names of our columns! That's right. We name the enumerators after the columns in our database to avoid the complexity and confusion that it may bring.
The ID enumerator will be used to store the database ID of the player.
The Name enumerator will be used to store the player's name.
The Password enumerator will be used to store the player's password.
The IP enumerator will be used to store the player's IP address.
The Admin enumerator will be used to store the player's Admin level.
The VIP enumerator will be used to store the player's VIP level.
The Money enumerator will be used to store the player's money.
The PosX enumerator will be used to store the X-position of the player.
The PosY enumerator will be used to store the Y-position of the player.
The PosZ enumerator will be used to store the Z-position of the player.
The PosA enumerator will be used to store the facing angle of the player.
Till this point, we haven't really connected to the database yet. The connection happens in OnGameModeInit. Why? Because we would like to load all the data (not player-related) when the gamemode initializes. We use the following snippet of code to do so:
Basically we can log everything using the mysql_log function, for the best result, we log everything by using the parameter (LOG_ALL). After that, we connect to our MySQL database. Remember that global variable? We were going to use that to store our connection. Here it comes to use! We assign that global variable to our connection.
mysql_errno will check for error. The if-statement goes like this: 'if there are errors then say that the connection failed, else say that the connection was successful".
Our next step is loading the data of a player that connects (to decide whether or not he is registered). We do this by using threaded queries. Queries can be visualized as a line of customers waiting to hand over their data. We thread these queries to secure the data going from the server to our MySQL database.
We start by declaring some variables that we are going to use within the callback. We use the query variable as our format variable. We use the playername variable to store the playername.
We then continue by retrieving the data, we don't use the regular format for various reasons one being the '%e' specifier which escapes strings (so that no MySQL code can be filled in; see many MySQL exploits). The MySQL line is quite self-explanatory: it SELECTs the Password column and the ID column FROM the accounts database WHERE the name column has a value of '%e' which is in this case the player's name and it limits the results to 1 entry.
It will then jump over to a function called 'OnAccountCheck' - in the form of a threaded query.
Start with forwarding your function. In this case, OnAccountCheck with playerid as the parameter. We then define two variables; rows and fields. Rows will hold the amount of returned rows from the query under OnPlayerConnect, fields will do the exact same thing but then with fields. The if statement translates to: if rows isn't equal to zero, then retrieve the value in the password column of that row and assign Player[playerid][Password] to it. Then it will assign Player[playerid][ID] to whatever value the query gave us in the 'ID' column of that row. When that's done it will show the dialogue to the player asking him/her to log in to the server. If the rows equal to zero then the player has no data in the database yet which means he hasn't registered yet. Therefore, it will show the dialog to register.
Last but not least we have to configure our dialogues. We do this with the OnDialogResponse callback.
We check the dialogue ID that the player is currently viewing, we do that by the use of a switch statement. I assume that you have heard/seen or used a switch statement before and that I don't have to go in-depth with it. Once we found the correct case, in the first example the login dialogue, we execute the code within that case.
The login dialogue: we kick the player when he clicks the 'Quit' button. We then declare the three variables that we will use: hashpass (to hash the inputtext), query (format variable) and playername (to store the player's name). We then put two of our variables to use; playername and hashpass. To check whether or not the player has filled in a matching password, we have to hash the input text first since the password is stored as a hash in our database. To compare the input text and the player's stored password, we use the function 'strcmp' - notice the exclamation point before the strcmp function, this represents the '== 0' at the end of the strcmp function. Why? The strcmp function returns 0 when the two compared strings match. If the hashed input text and the stored password match, we continue by loading the rest of the player's stored data. If the hashed input text and the stored password don't match, we send a message saying that it doesn't and we show the dialogue again so they can have another login attempt. Ideally, you would add a check for the amount of times the player has tried to log in so they can't float the server with login request to guess someone's password.
The register dialogue: just as with the login dialogue, we kick the player when he clicks the 'Quit' button. We then check whether the input text is less than 5 characters in length. If it is smaller than 5 characters in length, we let the player know that the password must at least contain more than 4 characters and we show the dialogue again to give the player another run at filling it his desired password. Right after doing that, we declare our variables: query (format variable), playername (to store the player's name) and playerip (to store the player's IP address). We then put two of our variables straight to use; playername and playerip. Notice that I am storing the hashed input text in Player[playerid][Password], you can use a fourth variable to store the hashed input text but you will have to assign the player variable to it any way. This is because otherwise the password will be insecure in the session that the player registered in. We then end the register dialogue with inserting the player's data into the database.
This is a query function of the login dialogue. Now as mentioned above, we have to load the rest of the player's data when he has entered a correct password. We can easily do that by using a threaded query in the login dialogue. Note: Threaded queries are always in the form of public functions so they must be forwarded!
We simply assign our global player variables to the retrieved value of whatever the database has stored. To retrieve the database value we use a function called 'cache_get_field_content_'. Since we have a range of value types, we must also include the value type we are trying to retrieve:
Notice the '0' as the first parameter of the function, this is simply the row ID in our database. When loading the amount of rows for the connecting player, we limited it to only 1 row. Just as with arrays we start counting from 0 and not from 1. The 0 in this case means the first row that we retrieved previously. The second parameter is in the form of a string and represents the column name. We end the callback with setting the player's money to the right value and notify him that he has logged in successfully.
This is the query function of the register dialogue. Just as with the login dialogue, the function that we call with our threaded query must always be in the form of a public function so they must be forwarded! What we do here is assign the 'ID' player variable to the ID that the database give him (in case he is the 32nd registered player, his ID will be 450).
To finish the registration or the login process, we set the player's position and facing angle in our server. There are two methods to do so. One method is to set the player's location when he spawns and the second method is to set the player's spawn information before spawning the player. What's the best? It doesn't matter (as far as I know) the only notable difference is that you don't use the OnPlayerSpawn callback for the second method (go to the afterword for an example of the second method).
Of course, we have to save the player's data on the moment that he disconnects so that he can continue his journey on the server where he left off. We do this under the 'OnPlayerDisconnect' callback (which is called when the player disconnects). We start off by declaring the variables that we are going to use; query (format variable) and Float:pos[] (to hold the player's coordinates). Notice the prefix: 'Float:'. This means that that the variable is going to be a floating integer (e.g.: 1.2). I use an array for this variable to make it more appealing and easier to use. You can leave the array out but you will have to declare PosX, PosY, PosZ and PosA all separately. We continue the saving process with putting our variables to use. For the X, Y and Z coordinates, we use a function 'GetPlayerPos'. Notice how the facing angle (A) is not in there. For the facing angle (A) we use a different function 'GetPlayerFacingAngle'. We then finish our saving process with updating the database with this newly retrieved information about the player. Since the player is disconnecting, using a threaded query with a function is unnecessary hence the empty fourth and fifth parameter.
Afterword
I would like to remind anyone that has reached this stage of this tutorial that, yes, I derived most of this tutorial from newbienoob's tutorial and that is exactly what it is: an updated version of his tutorial!
Mistakes and errors (both grammatically as script-wise) may be pointed out to me and I accept criticism. I will add a FAQ section to this tutorial once people start asking questions.
This has not been tested either. I will test it in the near future but I have little to no time as I have been writing this tutorial in the spare time that I was able to get in the past week. Even though, the explanations are conceptionally correct (I believe).
Thank you.
Credits:
newbienoob's tutorial for the concept / first version of the tutorial.
This is officially my first tutorial on this forum. I won't go through my background, instead I'd like to give some explanation on why I am posting this tutorial. First of all, newbienoob's tutorial does the job well. It gives users an idea on how MySQL within PAWN works (specifically registering and logging in). The thing that bothers me and probably a few other members is that his tutorial is not being maintained right now (he left SA:MP) because of that the tutorial could lack on important updates. Let us get straight to the tutorial.
TL ; DR: It could lack behind on important and essential updates of the plugins/includes that it uses.
Necessities
- The latest version of the MySQL plugin (GitHub).
- The latest version of the Whirlpool plugin (Forum topic).
- The latest version of XAMPP (Apache friends/XAMPP).
It is also recommended to check on the WIKI-page of the MySQL plugin for additional information regarding MySQL functions within the plugin: https://sampwiki.blast.hk/wiki/MySQL/R33.
Table of content
- Setting up the database (XAMPP) ;
- MySQL configurations in PAWN ;
- Creating your script
I will not be explaining what XAMPP exactly is, you can read all about it on their website. One thing you should know is that you can only run XAMPP through xampp-control.exe.
Extra note: pictures used in this tutorial are displayed as a thumbnail. Click on the picture to zoom in to it.
When launching XAMPP through xampp-control.exe, you should be able to see the following window:
For this tutorial and for SA:MP, we only need two modules: Apache and MySQL. Feel free to check what the other modules do for the sake of knowledge but we don't need them.
To initialize the Apache and the MySQL modules simply press the 'start' button under the 'actions' sub-title.
Note that Apache listens on port 80 and 443. A known application to interfere with this is Skype. I will show you how to run Skype and Apache all together in the FAQ section.
Great! The next step is creating your database and the required tables. To do so, simply click 'Admin' for the MySQL module.
Your standard browser should redirect you to the phpmyadmin page of your MySQL server (localhost/phpmyadmin).
Of course we don't need to go through all these options. We only need to create one database for this tutorial. Again, you can go through all the options for the sake of knowledge. There are two methods - easily visible - one being the 'new' button in the left sidebar and the second one being in the 'databases' tab. The 'new' button redirects to the 'databases' tab anyway so it doesn't matter what you choose.
You have to fill in the name of your database and click create when you did so. This will add your database to the left sidebar in between the rest of the databases. For the sake of the tutorial, we will call our database 'myserver'.
Fantastic, you have successful created your own MySQL database! We are not finished yet, though. The next step is create individual tables in our database to organize our data (Data for Players, vehicles, houses, jobs, weapons, ...). This can be done by clicking on your database in the left sidebar.
You will soon see that it asks you to create tables with a certain amount of columns. For the sake of this tutorial, we create a table called 'accounts' and it will store the account data of the player.
For integers use the data value type (INT).
For strings use the data value type (VARCHAR).
For floats use the data value type (FLOAT).
The basic register/login script needs a minimum of 10 columns (+ 1 for the VIP rank). The database ID for the player (which we will use to link other tables with this user), the player's name, the player's password, the player's IP, The player's administrator rank, the player's VIP rank, the player's money and the player's position (X, Y, Z, A). Go ahead and add the remaining columns to your table (till you have 11 columns).
Notice the index set to 'PRIMARY' and the A_I option checked. Basically we will use the ID column to identify the player in the database and for every new record, the ID goes up by one. For example: I made the first account on your server, my database ID is now 1. You make your account after I do, you ID will be 2. Click 'save' when you are ready to move on to the scripting part of this tutorial.
Congratulations! Your database has been created and configured successfully and now it is time to make it of use.
Scripting
We need two basic includes to make a server with a MySQL plugin. The a_samp include and of course the a_mysql include. You can start off by including both.
PHP код:
#include <a_samp>
#include <a_mysql>
PHP код:
#define MYSQL_HOST "localhost"
#define MYSQL_USER "root"
#define MYSQL_DATABASE "myserver"
#define MYSQL_PASSWORD ""
MYSQL_USER is the user of our MySQL database.
MYSQL_DATABASE is the name of the MySQL database that we created in the beginning of this tutorial.
MYSQL_PASSWORD is the password to authorize the connection. XAMPP has no default password to we can leave this blank.
PHP код:
#define SPAWN_X 10.0
#define SPAWN_Y 10.0
#define SPAWN_Z 14.0
#define SPAWN_A 0.0
SPAWN_X is the X-coordinate (East and West) in the GTA world.
SPAWN_Y is the Y-coordinate (North and South) in the GTA world.
SPAWN_Z is the Z-coordinate (Up and down) in the GTA world.
SPAWN_A is the A-coodinate (or the facing angle) in the GTA world.
To get your in game coordinates use /save. SA:MP saves the coordinates in a file under 'My Documents/GTA San Andreas User Files/SAMP'
For the register/login dialogues, I decided to not use defines but an enum. This method avoids dialogue ID collisions, meaning that you don't have to assign an ID to every dialogue you make.
PHP код:
enum
{
LoginDialog,
RegisterDialog
};
PHP код:
new
mysql;
PHP код:
native WP_Hash(buffer[], len, const str[]);
PHP код:
enum PlayerData
{
ID,
Name[MAX_PLAYER_NAME],
Password[129],
IP[16],
Admin,
VIP,
Money,
Float:posX,
Float:posY,
Float:posZ,
Float:posA
};
new Player[MAX_PLAYERS][PlayerData];
The ID enumerator will be used to store the database ID of the player.
The Name enumerator will be used to store the player's name.
The Password enumerator will be used to store the player's password.
The IP enumerator will be used to store the player's IP address.
The Admin enumerator will be used to store the player's Admin level.
The VIP enumerator will be used to store the player's VIP level.
The Money enumerator will be used to store the player's money.
The PosX enumerator will be used to store the X-position of the player.
The PosY enumerator will be used to store the Y-position of the player.
The PosZ enumerator will be used to store the Z-position of the player.
The PosA enumerator will be used to store the facing angle of the player.
Till this point, we haven't really connected to the database yet. The connection happens in OnGameModeInit. Why? Because we would like to load all the data (not player-related) when the gamemode initializes. We use the following snippet of code to do so:
PHP код:
public OnGameModeInit()
{
mysql_log(LOG_ALL);
mysql = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_DATABASE, MYSQL_PASSWORD);
if(mysql_errno() != 0)
{
printf("[MySQL] The connection has failed.");
}
else
{
printf("[MySQL] The connection was successful.");
}
return true;
}
mysql_errno will check for error. The if-statement goes like this: 'if there are errors then say that the connection failed, else say that the connection was successful".
Our next step is loading the data of a player that connects (to decide whether or not he is registered). We do this by using threaded queries. Queries can be visualized as a line of customers waiting to hand over their data. We thread these queries to secure the data going from the server to our MySQL database.
PHP код:
public OnPlayerConnect(playerid)
{
TogglePlayerSpectating(playerid, true);
new
query[128],
playername[MAX_PLAYER_NAME];
GetPlayerName(playerid, playername, sizeof(playername));
mysql_format(mysql, query, sizeof(query), "SELECT `Password`, `ID` FROM `accounts` WHERE `Name` = '%e' LIMIT 1", playername);
mysql_tquery(mysql, query, "OnAccountCheck", "i", playerid);
return true;
}
We then continue by retrieving the data, we don't use the regular format for various reasons one being the '%e' specifier which escapes strings (so that no MySQL code can be filled in; see many MySQL exploits). The MySQL line is quite self-explanatory: it SELECTs the Password column and the ID column FROM the accounts database WHERE the name column has a value of '%e' which is in this case the player's name and it limits the results to 1 entry.
It will then jump over to a function called 'OnAccountCheck' - in the form of a threaded query.
PHP код:
forward OnAccountCheck(playerid);
public OnAccountCheck(playerid)
{
new
rows,
fields;
cache_get_data(rows, fields, mysql);
if(rows)
{
cache_get_field_content(0, "Password", Player[playerid][Password], mysql, 129);
Player[playerid][ID] = cache_get_field_content_int(0, "ID");
ShowPlayerDialog(playerid, LoginDialog, DIALOG_STYLE_INPUT, "Login", "Welcome player!\nYour account has been found in our database. Please fill in your password:", "Login", "Quit");
}
else
{
ShowPlayerDialog(playerid, RegisterDialog, DIALOG_STYLE_INPUT, "Register", "Welcome player!\nYour account has not been registered yet. Please fill in your desired password:", "Register", "Quit");
}
return true;
}
Last but not least we have to configure our dialogues. We do this with the OnDialogResponse callback.
PHP код:
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch(dialogid)
{
case LoginDialog:
{
if(!response) Kick(playerid);
new
hashpass[129],
query[100],
playername[MAX_PLAYER_NAME];
GetPlayerName(playerid, playername, sizeof(playername));
WP_Hash(hashpass, sizeof(hashpass), inputtext);
if(!strcmp(hashpass, Player[playerid][Password]))
{
mysql_format(mysql, query, sizeof(query), "SELECT * FROM `accounts` WHERE `Name` = '%e' LIMIT 1", playername);
mysql_tquery(mysql, query, "OnAccountLoad", "i", playerid);
}
else
{
SendClientMessage(playerid, -1, "You have specified an incorrect password!");
ShowPlayerDialog(playerid, LoginDialog, DIALOG_STYLE_INPUT, "Login", "Welcome player!\nYour account has been found in our database. Please fill in your password:", "Login", "Quit");
}
}
case RegisterDialog:
{
if(!response) return Kick(playerid);
if(strlen(inputtext) < 5)
{
SendClientMessage(playerid, -1, "Your password must at least contain more than 4 characters.");
return ShowPlayerDialog(playerid, RegisterDialog, DIALOG_STYLE_INPUT, "Register", "Welcome player!\nYour account has not been registered yet. Please fill in your desired password:", "Register", "Quit");
}
new
query[512],
playername[MAX_PLAYER_NAME],
playerip[16];
GetPlayerName(playerid, playername, sizeof(playername));
GetPlayerIp(playerid, playerip, sizeof(playerip));
WP_Hash(Player[playerid][Password], 129, inputtext);
mysql_format(mysql, query, sizeof(query), "INSERT INTO `accounts` (`Name`, `Password`, `IP`, `Admin`, `VIP`, `Money`, `PosX`, `PosY`, `PosZ`, `PosA`) VALUES ('%e', '%e', '%e', 0, 0, 0, %f, %f, %f, %f)", playername, Player[playerid][Password], playerip, SPAWN_X, SPAWN_Y, SPAWN_Z, SPAWN_A);
mysql_tquery(mysql, query, "OnAccountRegister", "i", playerid);
}
}
return false; // For filterscripts..
}
The login dialogue: we kick the player when he clicks the 'Quit' button. We then declare the three variables that we will use: hashpass (to hash the inputtext), query (format variable) and playername (to store the player's name). We then put two of our variables to use; playername and hashpass. To check whether or not the player has filled in a matching password, we have to hash the input text first since the password is stored as a hash in our database. To compare the input text and the player's stored password, we use the function 'strcmp' - notice the exclamation point before the strcmp function, this represents the '== 0' at the end of the strcmp function. Why? The strcmp function returns 0 when the two compared strings match. If the hashed input text and the stored password match, we continue by loading the rest of the player's stored data. If the hashed input text and the stored password don't match, we send a message saying that it doesn't and we show the dialogue again so they can have another login attempt. Ideally, you would add a check for the amount of times the player has tried to log in so they can't float the server with login request to guess someone's password.
The register dialogue: just as with the login dialogue, we kick the player when he clicks the 'Quit' button. We then check whether the input text is less than 5 characters in length. If it is smaller than 5 characters in length, we let the player know that the password must at least contain more than 4 characters and we show the dialogue again to give the player another run at filling it his desired password. Right after doing that, we declare our variables: query (format variable), playername (to store the player's name) and playerip (to store the player's IP address). We then put two of our variables straight to use; playername and playerip. Notice that I am storing the hashed input text in Player[playerid][Password], you can use a fourth variable to store the hashed input text but you will have to assign the player variable to it any way. This is because otherwise the password will be insecure in the session that the player registered in. We then end the register dialogue with inserting the player's data into the database.
PHP код:
forward OnAccountLoad(playerid);
public OnAccountLoad(playerid)
{
Player[playerid][Admin] = cache_get_field_content_int(0, "Admin");
Player[playerid][VIP] = cache_get_field_content_int(0, "VIP");
Player[playerid][Money] = cache_get_field_content_int(0, "Money");
Player[playerid][posX] = cache_get_field_content_float(0, "PosX");
Player[playerid][posY] = cache_get_field_content_float(0, "PosY");
Player[playerid][posZ] = cache_get_field_content_float(0, "PosZ");
Player[playerid][posA] = cache_get_field_content_float(0, "PosA");
TogglePlayerSpectating(playerid, false);
GivePlayerMoney(playerid, Player[playerid][Money]);
SetSpawnInfo(playerid, 0, 23, Player[playerid][posX], Player[playerid][posY], Player[playerid][posZ], Player[playerid][posA], 0, 0, 0, 0, 0, 0);
SpawnPlayer(playerid);
SendClientMessage(playerid, -1, "You have successfully logged in.");
return true;
}
We simply assign our global player variables to the retrieved value of whatever the database has stored. To retrieve the database value we use a function called 'cache_get_field_content_'. Since we have a range of value types, we must also include the value type we are trying to retrieve:
- For values in the form of integers (INT): cache_get_field_content_int
- For values in the form of strings (VARCHAR): cache_get_field_content
- For values in the form of floating integers (FLOAT): cache_get_field_content_float
Notice the '0' as the first parameter of the function, this is simply the row ID in our database. When loading the amount of rows for the connecting player, we limited it to only 1 row. Just as with arrays we start counting from 0 and not from 1. The 0 in this case means the first row that we retrieved previously. The second parameter is in the form of a string and represents the column name. We end the callback with setting the player's money to the right value and notify him that he has logged in successfully.
PHP код:
forward OnAccountRegister(playerid);
public OnAccountRegister(playerid)
{
Player[playerid][ID] = cache_insert_id();
printf("[Registration] New account registered. Database ID: [%d]", Player[playerid][ID]);
TogglePlayerSpectating(playerid, false);
SetSpawnInfo(playerid, 0, 23, SPAWN_X, SPAWN_Y, SPAWN_Z, SPAWN_A, 0, 0, 0, 0, 0, 0);
SpawnPlayer(playerid);
return true;
}
PHP код:
public OnPlayerSpawn(playerid)
{
return true;
}
PHP код:
public OnPlayerDisconnect(playerid, reason)
{
new
query[128],
Float:pos[4];
GetPlayerPos(playerid, pos[0], pos[1], pos[2]);
GetPlayerFacingAngle(playerid, pos[3]);
mysql_format(mysql, query, sizeof(query), "UPDATE `accounts` SET `Money` = %d, `PosX` = %f, `PosY` = %f, `PosZ` = %f, `PosA` = %f WHERE `ID` = %d",
GetPlayerMoney(playerid), pos[0], pos[1], pos[2], pos[3], Player[playerid][ID]);
mysql_tquery(mysql, query, "", "");
return true;
}
Afterword
I would like to remind anyone that has reached this stage of this tutorial that, yes, I derived most of this tutorial from newbienoob's tutorial and that is exactly what it is: an updated version of his tutorial!
Mistakes and errors (both grammatically as script-wise) may be pointed out to me and I accept criticism. I will add a FAQ section to this tutorial once people start asking questions.
This has not been tested either. I will test it in the near future but I have little to no time as I have been writing this tutorial in the spare time that I was able to get in the past week. Even though, the explanations are conceptionally correct (I believe).
Thank you.
Credits:
newbienoob's tutorial for the concept / first version of the tutorial.