[Tutorial] [UPDATED 19/05/2015]MySQL Registration System [Threaded Queries + Whirlpool]
#1

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
  • Setting up the database (XAMPP) ;
  • MySQL configurations in PAWN ;
  • Creating your script
Setting up the database
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> 
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!

PHP код:
#define    MYSQL_HOST        "localhost"
#define    MYSQL_USER        "root"
#define    MYSQL_DATABASE    "myserver"
#define    MYSQL_PASSWORD    "" 
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.

PHP код:
#define    SPAWN_X  10.0
#define    SPAWN_Y  10.0
#define    SPAWN_Z  14.0
#define    SPAWN_A  0.0 
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.

PHP код:
enum
{
    
LoginDialog,
    
RegisterDialog
}; 
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.

PHP код:
new
    
mysql
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.

PHP код:
native WP_Hash(buffer[], len, const str[]); 
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... .

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]; 
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:
PHP код:
public OnGameModeInit()
{
    
mysql_log(LOG_ALL);
    
mysql mysql_connect(MYSQL_HOSTMYSQL_USERMYSQL_DATABASEMYSQL_PASSWORD);
    if(
mysql_errno() != 0)
    {
        
printf("[MySQL] The connection has failed.");
    }
    else
    {
        
printf("[MySQL] The connection was successful.");
    }
    return 
true;

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.

PHP код:
public OnPlayerConnect(playerid)
{
    
TogglePlayerSpectating(playeridtrue);
    new
        
query[128],
        
playername[MAX_PLAYER_NAME];
        
    
GetPlayerName(playeridplayernamesizeof(playername));
    
mysql_format(mysqlquerysizeof(query), "SELECT `Password`, `ID` FROM `accounts` WHERE `Name` = '%e' LIMIT 1"playername);
    
mysql_tquery(mysqlquery"OnAccountCheck""i"playerid);
    return 
true;

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.

PHP код:
forward OnAccountCheck(playerid);
public 
OnAccountCheck(playerid)
{
    new
        
rows,
        
fields;
    
cache_get_data(rowsfieldsmysql);
    
    if(
rows)
    {
        
cache_get_field_content(0"Password"Player[playerid][Password], mysql129);
        
Player[playerid][ID] = cache_get_field_content_int(0"ID");
        
ShowPlayerDialog(playeridLoginDialogDIALOG_STYLE_INPUT"Login""Welcome player!\nYour account has been found in our database. Please fill in your password:""Login""Quit");
    }
    else
    {
        
ShowPlayerDialog(playeridRegisterDialogDIALOG_STYLE_INPUT"Register""Welcome player!\nYour account has not been registered yet. Please fill in your desired password:""Register""Quit");
    }
    return 
true;

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.

PHP код:
public OnDialogResponse(playeriddialogidresponselistiteminputtext[])
{
    switch(
dialogid)
    {
        case 
LoginDialog:
        {
            if(!
responseKick(playerid);
            
            new
                
hashpass[129],
                
query[100],
                
playername[MAX_PLAYER_NAME];
            
GetPlayerName(playeridplayernamesizeof(playername));
            
WP_Hash(hashpasssizeof(hashpass), inputtext);
            if(!
strcmp(hashpassPlayer[playerid][Password]))
            {
                
mysql_format(mysqlquerysizeof(query), "SELECT * FROM `accounts` WHERE `Name` = '%e' LIMIT 1"playername);
                
mysql_tquery(mysqlquery"OnAccountLoad""i"playerid);
            }
            else
            {
                
SendClientMessage(playerid, -1"You have specified an incorrect password!");
                
ShowPlayerDialog(playeridLoginDialogDIALOG_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(playeridRegisterDialogDIALOG_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(playeridplayernamesizeof(playername));
            
GetPlayerIp(playeridplayeripsizeof(playerip));
            
WP_Hash(Player[playerid][Password], 129inputtext);
            
mysql_format(mysqlquerysizeof(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)"playernamePlayer[playerid][Password], playeripSPAWN_XSPAWN_YSPAWN_ZSPAWN_A);
            
mysql_tquery(mysqlquery"OnAccountRegister""i"playerid);
        }
    }
    return 
false// For filterscripts..

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.

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(playeridfalse);
    
GivePlayerMoney(playeridPlayer[playerid][Money]);
    
SetSpawnInfo(playerid023Player[playerid][posX], Player[playerid][posY], Player[playerid][posZ], Player[playerid][posA], 000000);
    
SpawnPlayer(playerid);
    
SendClientMessage(playerid, -1"You have successfully logged in.");
    return 
true;

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:
  • 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
The 'Admin' enumerator is an integer, hence 'cache_get_field_content_int'.
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(playeridfalse);
    
SetSpawnInfo(playerid023SPAWN_XSPAWN_YSPAWN_ZSPAWN_A000000);
    
SpawnPlayer(playerid);
    return 
true;

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).

PHP код:
public OnPlayerSpawn(playerid)
{
    return 
true;

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).

PHP код:
public OnPlayerDisconnect(playeridreason)
{
    new
        
query[128],
        
Float:pos[4];
        
    
GetPlayerPos(playeridpos[0], pos[1], pos[2]);
    
GetPlayerFacingAngle(playeridpos[3]);
    
    
mysql_format(mysqlquerysizeof(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(mysqlquery"""");
    
    return 
true;

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.
Reply
#2

- Checking about mysql racing would be really good as in rare cases players can get the data from the previous player.
- Selecting all the player's data on connect once and comparing the password afterwards, if it matches assigning to variables directly instead of executing another query.
- In cache_get_field_content function, you need to set max_len parameter when enum-array is used.
- cache_get_row/cache_get_row_* functions is slightly faster than cache_get_field_content/cache_get_field_content_* but you could probably explain both and let the user decide which one (just a suggestion).

Other than that, good job I guess. By the way, this may help few people: https://sampwiki.blast.hk/wiki/MySQL/R33
Reply
#3

Quote:
Originally Posted by Konstantinos
Посмотреть сообщение
- Checking about mysql racing would be really good as in rare cases players can get the data from the previous player.
- Selecting all the player's data on connect once and comparing the password afterwards, if it matches assigning to variables directly instead of executing another query.
- In cache_get_field_content function, you need to set max_len parameter when enum-array is used.
- cache_get_row/cache_get_row_* functions is slightly faster than cache_get_field_content/cache_get_field_content_* but you could probably explain both and let the user decide which one (just a suggestion).

Other than that, good job I guess. By the way, this may help few people: https://sampwiki.blast.hk/wiki/MySQL/R33
Thank you for the suggestions and the additional information, I will add those in as soon as I possibly can (preferably tomorrow).
Reply
#4

Very detailed tutorial,good job
Reply
#5

Quote:
Код:
    Float:PosX, 
    Float:PosY, 
    Float:PosZ, 
    Float:PosA
Sigh...
Reply
#6

Quote:
Originally Posted by sammp
Посмотреть сообщение
Sigh...
Could you give me more information on that sigh of yours?
Reply
#7

Quote:
Originally Posted by Overhaul
Посмотреть сообщение
Could you give me more information on that sigh of yours?
He's referring to the non-use of an array instead.

pawn Код:
new Float:pos[4];
Reply
#8

Quote:
Originally Posted by SKAzini
Посмотреть сообщение
He's referring to the non-use of an array instead.
I soon realised that after replying to it. Either way, I believe there is nothing wrong with my example.
Reply
#9

I had trouble with the code in a way that it logged me in even do I had typed the wrong password. Then I checked the value of Player[playerid][Password], and it was null. Then I checked the wiki for cache_get_field_content and there it says: "You have to provide the size (max_len) by yourself if you use an enum-array as destination.". So I changed it to this:
pawn Код:
cache_get_field_content(0, "Password", Player[playerid][Password], 1, 129);
Reply
#10

Quote:
Originally Posted by Antonio144
Посмотреть сообщение
I had trouble with the code in a way that it logged me in even do I had typed the wrong password. Then I checked the value of Player[playerid][Password], and it was null. Then I checked the wiki for cache_get_field_content and there it says: "You have to provide the size (max_len) by yourself if you use an enum-array as destination.". So I changed it to this:
pawn Код:
cache_get_field_content(0, "Password", Player[playerid][Password], 1, 129);
Thank you for notifying me about this as I forgot to include this and an alternative method to do this.

Wiki method:
PHP код:
cache_get_field_content(0"Password"Player[playerid][Password], mysql129); 
Alternative:
PHP код:
    new
        
database_password[129];
    
cache_get_field_content(0"Password"database_password);
    
Player[playerid][Password][0] = EOS;
    
strcat(Player[playerid][Password], database_passwordsizeof(database_password)); 
I will edit that right away!
Reply
#11

This isn't using an old version of the MySQL plugin is it? Getting a lot of undefined symbol errors when compiling.

PHP Code:
vrp.pwn(81) : error 017undefined symbol "mysql_tquery"
vrp.pwn(93) : warning 202number of arguments does not match definition
vrp
.pwn(94) : error 017undefined symbol "cache_get_field_content_int"
vrp.pwn(113) : error 035argument type mismatch (argument 3)
vrp.pwn(115) : error 017undefined symbol "mysql_tquery"
vrp.pwn(152) : error 035argument type mismatch (argument 3)
vrp.pwn(153) : error 017undefined symbol "mysql_tquery"
vrp.pwn(178) : error 035argument type mismatch (argument 3)
vrp.pwn(179) : error 017undefined symbol "mysql_tquery"
vrp.pwn(188) : error 017undefined symbol "cache_get_field_content_int"
vrp.pwn(189) : error 017undefined symbol "cache_get_field_content_int"
vrp.pwn(190) : error 017undefined symbol "cache_get_field_content_int"
vrp.pwn(191) : error 017undefined symbol "cache_get_field_content_float"
vrp.pwn(192) : error 017undefined symbol "cache_get_field_content_float"
vrp.pwn(193) : error 017undefined symbol "cache_get_field_content_float"
vrp.pwn(194) : error 017undefined symbol "cache_get_field_content_float"
vrp.pwn(198) : warning 217loose indentation
vrp
.pwn(204) : error 017undefined symbol "cache_insert_id" 
Reply
#12

Quote:
Originally Posted by BR3TT
View Post
This isn't using an old version of the MySQL plugin is it? Getting a lot of undefined symbol errors when compiling.

PHP Code:
vrp.pwn(81) : error 017undefined symbol "mysql_tquery"
vrp.pwn(93) : warning 202number of arguments does not match definition
vrp
.pwn(94) : error 017undefined symbol "cache_get_field_content_int"
vrp.pwn(113) : error 035argument type mismatch (argument 3)
vrp.pwn(115) : error 017undefined symbol "mysql_tquery"
vrp.pwn(152) : error 035argument type mismatch (argument 3)
vrp.pwn(153) : error 017undefined symbol "mysql_tquery"
vrp.pwn(178) : error 035argument type mismatch (argument 3)
vrp.pwn(179) : error 017undefined symbol "mysql_tquery"
vrp.pwn(188) : error 017undefined symbol "cache_get_field_content_int"
vrp.pwn(189) : error 017undefined symbol "cache_get_field_content_int"
vrp.pwn(190) : error 017undefined symbol "cache_get_field_content_int"
vrp.pwn(191) : error 017undefined symbol "cache_get_field_content_float"
vrp.pwn(192) : error 017undefined symbol "cache_get_field_content_float"
vrp.pwn(193) : error 017undefined symbol "cache_get_field_content_float"
vrp.pwn(194) : error 017undefined symbol "cache_get_field_content_float"
vrp.pwn(198) : warning 217loose indentation
vrp
.pwn(204) : error 017undefined symbol "cache_insert_id" 
Update your plugin and include to MySQL R39-3.
Reply
#13

Quote:
Originally Posted by AndySedeyn
View Post
Update your plugin and include to MySQL R39-3.
I knew it was that, I must have replaced the wrong folder, it's working as it should now... Thanks for that, I knew it was something to do with outdated plugins. Glad I tried it again.

An update though, it's now doing it for 'GetPlayerCash', is this supposed to be in there?

GetPlayerCash(playerid), pos[0], pos[1], pos[2], pos[3], Player[playerid][ID]);

If so what plugin does this go under, I only have the a_mysql and a_samp and whirlpool atm.

EDIT: I changed it to GetPlayerMoney (assuming you just had cash and money mixed around) and it compiled properly.

EDIT 2: Everythings running, plugins running, wamp running, dialog box pops up and the right credentials are inside the script, but whenever I register an account, nothing is being sent to the database.

PHP Code:
[18:25:04] [DEBUGmysql_tquery connection1query"INSERT INTO `accounts` (`Username`, `Password`, `IP`, `Admin`, `"callback"OnAccountRegister"format"i"
[18:25:04] [DEBUGCMySQLQuery::Execute[OnAccountRegister] - starting query execution
[18:25:04] [ERRORCMySQLQuery::Execute[OnAccountRegister] - (error #1064) You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
[18:25:04] [DEBUGCMySQLQuery::Execute[OnAccountRegister] - error will be triggered in OnQueryError 
Reply
#14

Quote:
Originally Posted by BR3TT
View Post
PHP Code:
[18:25:04] [DEBUGmysql_tquery connection1query"INSERT INTO `accounts` (`Username`, `Password`, `IP`, `Admin`, `"callback"OnAccountRegister"format"i"
[18:25:04] [DEBUGCMySQLQuery::Execute[OnAccountRegister] - starting query execution
[18:25:04] [ERRORCMySQLQuery::Execute[OnAccountRegister] - (error #1064) You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
[18:25:04] [DEBUGCMySQLQuery::Execute[OnAccountRegister] - error will be triggered in OnQueryError 
It is possibly the size of the query variable, which is not big enough.
Change its size from 300 to 512.

PHP Code:
    new
        
query[512]; 
Reply
#15

Quote:
Originally Posted by BR3TT
View Post
An update though, it's now doing it for 'GetPlayerCash', is this supposed to be in there?

GetPlayerCash(playerid), pos[0], pos[1], pos[2], pos[3], Player[playerid][ID]);

If so what plugin does this go under, I only have the a_mysql and a_samp and whirlpool atm.

EDIT: I changed it to GetPlayerMoney (assuming you just had cash and money mixed around) and it compiled properly.
I edited the tutorial. I, indeed, mixed up the SA:MP function to get the player's money with my own function. Thank you for notifying me about it!
Reply
#16

Quote:
Originally Posted by AndySedeyn
View Post
It is possibly the size of the query variable, which is not big enough.
Change its size from 300 to 512.

PHP Code:
    new
        
query[512]; 
Yes that fixed it, thanks a lot!

OP I reckon you should change this as well
Reply
#17

You can login with wrong password. I have added, database_password. but still you can login with wrong password. And Also, It gives you more money. I mean, If you have $5 and quitted your game and then re-join then it gives you $10 instead of $5. Would you like to check it . It should be SetPlayerMoney instead of GivePlayerMoney.
Reply
#18

Quote:
Originally Posted by STONEGOLD
View Post
You can login with wrong password. I have added, database_password. but still you can login with wrong password. And Also, It gives you more money. I mean, If you have $5 and quitted your game and then re-join then it gives you $10 instead of $5. Would you like to check it . It should be SetPlayerMoney instead of GivePlayerMoney.
It should, indeed, be SetPlayerMoney. I have edited that. There was also a missing bracket, which I added now.
Reply
#19

Can you login with any password now? Or did you fix it?
Reply
#20

@EDIT: sorry, i'm a idiot, i was not adding includes only the plugins.
Reply


Forum Jump:


Users browsing this thread: 4 Guest(s)