PDA

View Full Version : [Tut] MySql - Save/Load/Usefull Things (Xp Mod)


grimvh2
07-17-2010, 18:43
Hello there,

Ive decided to write this down as an addition to the other tutorial here :
http://forums.alliedmods.net/showthread.php?t=66497

But then I would be using MySql. I will try to keep it simple and focus more on the mysql part then on the xp mod itself.

Be aware, when reading this you should have basic knowledge about pawn coding, I will try to learn you something about mysql coding on this tutorial.

We will start with a main plugin :


#include <amxmodx>

#define PLUGIN "Tutorial"
#define VERSION "1.0"
#define AUTHOR "Grim"

new iExp[33]

// Pcvar's
new cKill
new cHeadshot
new cDeath

public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)

register_event("DeathMsg", "Event_DeathMsg", "a") // Register death event

// register the Pcvar's
cKill = register_cvar("exp_kill", "2")
cHeadshot = register_cvar("exp_headshot", "4")
cDeath = register_cvar("exp_death", "1")
}

public Event_DeathMsg()
{
new iKiller = read_data(1) // read the data to get the killer and victim
new iVictim = read_data(2)

if(is_user_alive(iKiller)) // Check if the killer is alive in case he killed himself
{
if(read_data(3)) // Check if it was a headshot
{
iExp[iKiller] += get_pcvar_num(cHeadshot) // Add the amount of the Pcvar to iExp
}
else
{
iExp[iKiller] += get_pcvar_num(cKill)
}
}
iExp[iVictim] -= get_pcvar_num(cDeath) // Decrease the amount of the Pcvar from iExp
}
Then we start adding mysql to it.

First include sqlx

#include <sqlx>
Then we start to create a mysql init


// Credits to hawk552 from hes old tutorial

public MySql_Init()
{
// we tell the API that this is the information we want to connect to,
// just not yet. basically it's like storing it in global variables
g_SqlTuple = SQL_MakeDbTuple(Host,User,Pass,Db)

// ok, we're ready to connect
new ErrorCode,Handle:SqlConnection = SQL_Connect(g_SqlTuple,ErrorCode,g_Error,char smax(g_Error))
if(SqlConnection == Empty_Handle)
// stop the plugin with an error message
set_fail_state(g_Error)

new Handle:Queries
// we must now prepare some random queries
Queries = SQL_PrepareQuery(SqlConnection,"CREATE TABLE IF NOT EXISTS tutorial (steamid varchar(32),exp INT(11))")

if(!SQL_Execute(Queries))
{
// if there were any problems the plugin will set itself to bad load.
SQL_QueryError(Queries,g_Error,charsmax(g_Err or))
set_fail_state(g_Error)

}

// Free the querie
SQL_FreeHandle(Queries)

// you free everything with SQL_FreeHandle
SQL_FreeHandle(SqlConnection)
}
To prevent errors we free the handle at a mapend


public plugin_end()
{
// free the tuple - note that this does not close the connection,
// since it wasn't connected in the first place
SQL_FreeHandle(g_SqlTuple)
}
Now we need to load and save the player hes stats


public Load_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

new Data[1]
Data[0] = id

//we will now select from the table `tutorial` where the steamid match
format(szTemp,charsmax(szTemp),"SELECT * FROM `tutorial` WHERE (`tutorial`.`steamid` = '%s')", szSteamId)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)
}

public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
{
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
}
else if(FailState == TQUERY_QUERY_FAILED)
{
log_amx("Load Query failed. [%d] %s", Errcode, Error)
}

new id
id = Data[0]

if(SQL_NumResults(Query) < 1)
{
//.if there are no results found

new szSteamId[32]
get_user_authid(id, szSteamId, charsmax(szSteamId)) // get user's steamid

// if its still pending we can't do anything with it
if (equal(szSteamId,"ID_PENDING"))
return PLUGIN_HANDLED

new szTemp[512]

// now we will insturt the values into our table.
format(szTemp,charsmax(szTemp),"INSERT INTO `tutorial` ( `steamid` , `exp`)VALUES ('%s','0');",szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
else
{
// if there are results found
iExp[id] = SQL_ReadResult(Query, 1)
}

return PLUGIN_HANDLED
}

public Save_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

// Here we will update the user hes information in the database where the steamid matches.
format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i' WHERE `tutorial`.`steamid` = '%s';",iExp[id], szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
Now our final code will look like this :


#include <amxmodx>
#include <sqlx>

#define PLUGIN "Tutorial"
#define VERSION "1.0"
#define AUTHOR "Grim"

// Ur Mysql Information
new Host[] = "hostname"
new User[] = "username"
new Pass[] = "password"
new Db[] = "database"


new Handle:g_SqlTuple
new g_Error[512]


new iExp[33]

// Pcvar's
new cKill
new cHeadshot
new cDeath

public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)

register_event("DeathMsg", "Event_DeathMsg", "a") // Register death event

// register the Pcvar's
cKill = register_cvar("exp_kill", "2")
cHeadshot = register_cvar("exp_headshot", "4")
cDeath = register_cvar("exp_death", "1")

set_task(1.0, "MySql_Init") // set a task to activate the mysql_init
}

public MySql_Init()
{
// we tell the API that this is the information we want to connect to,
// just not yet. basically it's like storing it in global variables
g_SqlTuple = SQL_MakeDbTuple(Host,User,Pass,Db)

// ok, we're ready to connect
new ErrorCode,Handle:SqlConnection = SQL_Connect(g_SqlTuple,ErrorCode,g_Error,char smax(g_Error))
if(SqlConnection == Empty_Handle)
// stop the plugin with an error message
set_fail_state(g_Error)

new Handle:Queries
// we must now prepare some random queries
Queries = SQL_PrepareQuery(SqlConnection,"CREATE TABLE IF NOT EXISTS tutorial (steamid varchar(32),exp INT(11))")

if(!SQL_Execute(Queries))
{
// if there were any problems
SQL_QueryError(Queries,g_Error,charsmax(g_Err or))
set_fail_state(g_Error)

}

// close the handle
SQL_FreeHandle(Queries)

// you free everything with SQL_FreeHandle
SQL_FreeHandle(SqlConnection)
}

public plugin_end()
{
// free the tuple - note that this does not close the connection,
// since it wasn't connected in the first place
SQL_FreeHandle(g_SqlTuple)
}

public Load_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

new Data[1]
Data[0] = id

//we will now select from the table `tutorial` where the steamid match
format(szTemp,charsmax(szTemp),"SELECT * FROM `tutorial` WHERE (`tutorial`.`steamid` = '%s')", szSteamId)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)
}

public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
{
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
}
else if(FailState == TQUERY_QUERY_FAILED)
{
log_amx("Load Query failed. [%d] %s", Errcode, Error)
}

new id
id = Data[0]

if(SQL_NumResults(Query) < 1)
{
//.if there are no results found

new szSteamId[32]
get_user_authid(id, szSteamId, charsmax(szSteamId)) // get user's steamid

// if its still pending we can't do anything with it
if (equal(szSteamId,"ID_PENDING"))
return PLUGIN_HANDLED

new szTemp[512]

// now we will insturt the values into our table.
format(szTemp,charsmax(szTemp),"INSERT INTO `tutorial` ( `steamid` , `exp`)VALUES ('%s','0');",szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
else
{
// if there are results found
iExp[id] = SQL_ReadResult(Query, 1)
}

return PLUGIN_HANDLED
}

public Save_MySql(id)
{
new szSteamId[32], szName[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

// Here we will update the user hes information in the database where the steamid matches.
format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i' WHERE `tutorial`.`steamid` = '%s';",iExp[id], szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}

public IgnoreHandle(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
SQL_FreeHandle(Query)

return PLUGIN_HANDLED
}

public client_putinserver(id)
{
Load_MySql(id)
}

public client_disconnect(id)
{
Save_MySql(id)
}

public Event_DeathMsg()
{
new iKiller = read_data(1) // read the data to get the killer and victim
new iVictim = read_data(2)

if(is_user_alive(iKiller)) // Check if the killer is alive in case he killed himself
{
if(read_data(3))
{
iExp[iKiller] += get_pcvar_num(cHeadshot) // Add the amount of the Pcvar to iExp
}
else
{
iExp[iKiller] += get_pcvar_num(cKill)
}
}
iExp[iVictim] -= get_pcvar_num(cDeath) // Decrease the amount of the Pcvar from iExp
}
Credits to hawk552 from hes old tutorial -
http://forums.alliedmods.net/showthread.php?t=46779&highlight=mysql

Good to know

Get a ranking :


public Show_Rank(id) // register cmd to this function
{
for(new i; i < MaxPlayers; i++)
{
if(is_user_connected(i))
Save_MySql(i) // Save all stats to get the correct rank
}

new Data[1]
Data[0] = id

new szTemp[512]
format(szTemp,charsmax(szTemp),"SELECT COUNT(*) FROM `tutorial` WHERE `exp` >= %d", Exp[id])
// Select the count where the exp is matching or higher (Incase of equal exp)
SQL_ThreadQuery(g_SqlTuple,"Sql_Rank",szTemp,Data,1)

return PLUGIN_CONTINUE
}

public Sql_Rank(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
else if(FailState == TQUERY_QUERY_FAILED)
log_amx("Load Query failed. [%d] %s", Errcode, Error)

new count = 0
count = SQL_ReadResult(Query,0)
if(count == 0)
count = 1

new id
id = Data[0]

client_print(id, print_chat, "You're rank is %i with %i exp", count, Exp[id]);

return PLUGIN_HANDLED
}
How to store floats:


//example
Queries[0] = SQL_PrepareQuery(SqlConnection, "CREATE TABLE IF NOT EXISTS %s (steamid varchar(32), yourfloat FLOAT(11,3))", szTableName);

// FLOAT(11,3)
// 11 = maximum number of digits
// 3 = maximum number of digits behind the decimal point

// Load data

new Float:yourfloat
SQL_ReadResult( Query, Column, yourfloat );
How to delete rows:


new szTemp[256], Data[1];

Data[0] = id

format(szTemp,charsmax(szTemp),"DELETE FROM `%s` WHERE `steamid` = '%s'", szMainTable, szSteamid);
SQL_ThreadQuery(SqlTuple,"IgnoreHandle",szTemp,Data,1);
ToDo List

- Adding more examples.


Common mistakes

- Ur mysql database does not support external connections.
- 1 of the 4 informations you fill in is not correct. (username, password, hostname, databasename)
- Ur created table differences from the updating you are doing the a record (look @ https://forums.alliedmods.net/showthread.php?t=133063 )



Any suggestions to add? Have u seen mistakes?
Please report them.

Greetings Grim

MMYTH
07-17-2010, 19:45
nice

alan_el_more
07-17-2010, 20:58
Good Job :D

lucas_7_94
07-17-2010, 21:29
Great work

PD: try to add when the server shutting down ( like plugin_end() or FM_GameShutdown , etc)

Alucard^
07-17-2010, 21:42
Really good job, at the moment i don't need to use mysql but i will need this in the future, thanks.

LudaGe
07-18-2010, 05:11
GJ man
It is Really Necessary
thx!

zeus
07-18-2010, 05:31
Sweet :o

grimvh2
07-18-2010, 07:32
Thanks for the positive comments, I will add some usefull things later.

AfteR.
07-18-2010, 17:06
Nice one. Good job :D

Kreation
07-18-2010, 18:01
Thanks for this, I might be able to use this in the future. Good job. :D

shuttle_wave
07-21-2010, 05:43
Wow. Nice TUT grim. would be cool as if u had a tutorial on how to extract those data from mysql and show it on the site :D. Good Job A++++

Hawk552
07-21-2010, 09:20
I think ErrorCode can be non-zero even when FailState is not TQUERY_QUERY_FAILED.

lucas_7_94
07-21-2010, 12:02
i have one cuestion , its not the same this

if(SQL_NumResults(Query) < 1) that this

if(!SQL_NumResults(Query)) ?

grimvh2
07-21-2010, 17:20
i have one cuestion , its not the same this

if(SQL_NumResults(Query) < 1) that this

if(!SQL_NumResults(Query)) ?

Pure theoreticly, the first is "if lower then 1" the other is "if equals 0". When there are bugs, strange things can occur and the first is the safest.

grimvh2
07-21-2010, 17:22
I think ErrorCode can be non-zero even when FailState is not TQUERY_QUERY_FAILED.

if the query did'nt failed and the connection also did'nt failed, the connection should be succesfull?

edit: sorry forgot about my other post.

Hawk552
07-21-2010, 17:49
if the query did'nt failed and the connection also did'nt failed, the connection should be succesfull?

edit: sorry forgot about my other post.

It appears that the error code can only be non-zero when the fail state is TQUERY_QUERY_FAILED. See here:


73 bool MysqlQuery::ExecuteR(QueryInfo *info, char *error, size_t maxlength)
74 {
75 int err;
76
77 if ( (err=mysql_real_query(m_pDatabase->m_pMysql, m_QueryString, (unsigned long)m_QueryLen)) )
78 {
79 info->errorcode = mysql_errno(m_pDatabase->m_pMysql);
80 info->success = false;
81 info->affected_rows = 0;
82 info->rs = NULL;
83 if (error && maxlength)
84 {
85 snprintf(error, maxlength, "%s", mysql_error(m_pDatabase->m_pMysql));
86 }
87 }
88 else
89 {
90 MYSQL_RES *res = mysql_store_result(m_pDatabase->m_pMysql);
91 if (!res)
92 {
93 if (mysql_field_count(m_pDatabase->m_pMysql) > 0)
94 {
95 //error !111!!11
96 info->errorcode = mysql_errno(m_pDatabase->m_pMysql);
97 info->success = false;
98 info->affected_rows = 0;
99 info->rs = NULL;
100 } else {
101 info->errorcode = 0;
102 info->success = true;
103 info->affected_rows = mysql_affected_rows(m_pDatabase->m_pMysql);
104 info->rs = NULL;
105 }
106 } else {
107 info->errorcode = 0;
108 info->success = true;
109 info->affected_rows = mysql_affected_rows(m_pDatabase->m_pMysql);
110 MysqlResultSet *rs = new MysqlResultSet(res, m_pDatabase->m_pMysql);
111 info->rs = rs;
112 }
113 }
114
115 return info->success;
116 }


That means you can ignore my comment.

shuttle_wave
07-22-2010, 00:22
Queries = SQL_PrepareQuery(SqlConnection,"CREATE TABLE IF NOT EXISTS tutorial (steamid varchar(32),exp INT(11))")

i dont get that bit. (steamid varchar(32),exp INT(11)) thats the bit i dont get. someone please explain

grimvh2
07-22-2010, 04:47
As siple as it is. At the mapstart we run that query, its going to look if the table already exists in the database. if not it will create a table with 2 fields, steamid and exp. varchar(32) is the same as "new String[32]" and I guess you know what an int is.

NzGamers
07-22-2010, 05:22
Nice Tutorial man. I see u explaining to Shuttle_Wave but im not very advance so wats exp INT(11)

grimvh2
07-22-2010, 05:43
exp is a field. When u save a record (a players steam id) you can set hes field. 1 field is hes steamid, the other is the amount of exp, we call the field exp and the player hes exp will be stored in there. an INT is just simply a number and the 11 is how many numbers it can contain. for example the max is 99.999.999.999

shuttle_wave
07-22-2010, 08:07
As siple as it is. At the mapstart we run that query, its going to look if the table already exists in the database. if not it will create a table with 2 fields, steamid and exp. varchar(32) is the same as "new String[32]" and I guess you know what an int is.

yeap. i get it now. ty vm

grimvh2
07-22-2010, 11:07
Updated.

Kureno
07-22-2010, 16:23
Nice Tutorial Grim, good Job :)

benjibau
07-23-2010, 14:34
an error with the rank i think because it's always first in rank:cry::cry:

but nice tutorial

grimvh2
07-23-2010, 17:32
an error with the rank i think because it's always first in rank:cry::cry:

but nice tutorial

Show ur code. Ask other players to say rank, might be because of equal scores. Its running flawless on my servers.

grimvh2
08-05-2010, 11:27
you should check if the victim is not the killer, because the killer's XP would increase if it's suicided

No, I check if the killer is still alive.

Alucard^
08-05-2010, 18:26
But grimvh2, isn't better if you use Killer != Victim instead of checking if user is alive using a native? i know is not a big deal but well...

vL.
08-05-2010, 23:37
Thanks grimvh2, very good tutorial and usefull.

grimvh2
08-08-2010, 05:25
But grimvh2, isn't better if you use Killer != Victim instead of checking if user is alive using a native? i know is not a big deal but well...

I think in some cases where the player kills him self, the killer and victim doesnt always return the same, I'm not sure but I guess in some cases with func_doors, trigger_hurt, drown, ... Anyway its not such big deal.

katna
08-20-2010, 01:43
why should i use my sql instead of nvault, is it better?

Alucard^
08-20-2010, 11:41
why should i use my sql instead of nvault, is it better?

As far as i know is more faster than nvault and others files systems... and also you can combine mysql with others systems like with forum or others.

grimvh2
08-22-2010, 13:05
The only thing nvault could be usefull for is a few data to save. nVault is slower and when u get to much data in, ur server will lag. Mysql doesnt cause lag ( when its done good ). Can handle much more data and can be used for websites ( online top 15 or w/e ).

Nextra
08-23-2010, 16:55
The MySQL data storing and retrieving functions are much more powerful than those of nVault. For basic data storing this does not make that much of a difference, though. That's why MySQL tutorials are sometimes problematic. MySQL is so powerful that you can not cover all of it. If someone plans to use MySQL he will often have to find more sources and really learn the syntax of MySQL queries. Imho it would be much more helpful to provide a set of stocks that would make MySQL as easy to use as nVault.

A huge difference is that a real MySQL database (not SQLite) is entirely seperate from the server itself. Handling huge data structures can cause problems with nVault while MySQL, especially when you use threaded querying, will not use any resources of the server.

Another nice thing is, that you can often make your plugin compatible with MySQL and SQLite at the same time. That way the user can use an external database, necessary for integration into actual websites, but also fall back to local server-storage if no MySQL database is at hand.


Suggestions for the tutorial:
- I believe that the tuple variable is not updated by SQL_Connect so you don't need to check it everytime but should after the SQL_MakeDbTuple call instead.
- You can remove most SQL_Connect calls anyway. This is not necessary for threaded querying.
- In the save function you don't use the name of the user. You should remove the variable and the get_user_name call.
- However, if you plan to use the name, you should cover string escaping aswell.
- Use formatex where applicable (read: everywhere in this tutorial).
- Think about statics and a global query buffer.
- Any reason to delay MySQL_Init for that long?

That's all I can see right now.

grimvh2
09-03-2010, 05:10
The MySQL data storing and retrieving functions are much more powerful than those of nVault. For basic data storing this does not make that much of a difference, though. That's why MySQL tutorials are sometimes problematic. MySQL is so powerful that you can not cover all of it. If someone plans to use MySQL he will often have to find more sources and really learn the syntax of MySQL queries. Imho it would be much more helpful to provide a set of stocks that would make MySQL as easy to use as nVault.

A huge difference is that a real MySQL database (not SQLite) is entirely seperate from the server itself. Handling huge data structures can cause problems with nVault while MySQL, especially when you use threaded querying, will not use any resources of the server.

Another nice thing is, that you can often make your plugin compatible with MySQL and SQLite at the same time. That way the user can use an external database, necessary for integration into actual websites, but also fall back to local server-storage if no MySQL database is at hand.


Suggestions for the tutorial:
- I believe that the tuple variable is not updated by SQL_Connect so you don't need to check it everytime but should after the SQL_MakeDbTuple call instead.
- You can remove most SQL_Connect calls anyway. This is not necessary for threaded querying.
- In the save function you don't use the name of the user. You should remove the variable and the get_user_name call.
- However, if you plan to use the name, you should cover string escaping aswell.
- Use formatex where applicable (read: everywhere in this tutorial).
- Think about statics and a global query buffer.
- Any reason to delay MySQL_Init for that long?

That's all I can see right now.

Thx for the advice.

What do you mean with a global query buffer?

Nextra
09-03-2010, 06:05
Just like you use a global variable to store errors you can use a global variable to store the queries you are formatting. There's no reason to create a new 512 variable everytime you need to make a query.

GuessWhat
10-31-2010, 01:41
how do i get an sql server? what is hostname, pass ,db ? cause i got a plugin fail to load

FEELiFE
10-31-2010, 02:28
How can I get the XP of a player and add a prefix before the player's name based on the XP? (prefix in chat messages)

Exolent[jNr]
10-31-2010, 12:59
how do i get an sql server? what is hostname, pass ,db ? cause i got a plugin fail to load

You have to get one with web hosting. Find a website that has free SQL databases or rent web hosting that has one.

GuessWhat
11-02-2010, 01:43
how i can update many field at the same time?
i have a database which contain steamid, goals, exp, points, exp, level, rank and so on. how can i update them all at once?

new goals[33]
new iExp[33]
new points[33]
new level[33]
new rank[33]
...
...
...
format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i' WHERE `tutorial`.`steamid` = '%s';",iExp[id], szSteamId)

grimvh2
11-02-2010, 13:45
format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i', `otherthing` = '%i', `astring` = '%s' WHERE `tutorial`.`steamid` = '%s';",iExp[id], otherthing[id], string[id] szSteamId)

Get it?

Jacob
11-03-2010, 02:40
format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i', `otherthing` = '%i', `astring` = '%s' WHERE `tutorial`.`steamid` = '%s';",iExp[id], otherthing[id], string[id] szSteamId)

Get it?
can you add a top15 for the Tut.

benjibau
11-03-2010, 07:23
can you add a top15 for the Tut.

Nice idea

Exolent[jNr]
11-03-2010, 13:35
can you add a top15 for the Tut.

It's only a simple query. The rest is things you should know from the tutorial (or another SQL tutorial) which is reading the 15 top players.

SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;

SELECT steamid, exp - Gets the steamid and exp from the table for each player
FROM tutorial - Gets data from tutorial table
ORDER BY exp DESC - Orders by the row's "exp" value descending so that the highest exp is first
LIMIT 15 - Limit 15 players in the results so if there are more than 15 they aren't used.

After executing this query, you would just need a simple loop:

new szSteamID[ 35 ], iExp;
new iPos = 1;

while( SQL_MoreResults( hQuery ) )
{
// SELECT steamid, exp
// steamid is first, so its column number is 0
// then exp, so its column number is 1
SQL_ReadResult( hQuery, 0, szSteamID, charsmax( szSteamID ) );
iExp = SQL_ReadResult( hQuery, 1 );

// print the player's top15 position and data
server_print( "#%i: %s - %i", iPos, szSteamID, iExp );

// increase to next top15 position and go to next row
iPos++;
SQL_NextRow( hQuery );
}

benjibau
11-03-2010, 14:37
Exolent i think if more good with board. I think is that ?

public ShowTop15(id)
{
new szSteamID[ 35 ], iExp;
new iPos = 1;
new MenuBody[512], len;
len = format(MenuBody, 511, "\yStats TOP 15^n";)

Place++
SQL_ReadResult(Query,1,Name,32)
Points = SQL_ReadResult(Query,2)

format(szTemp,charsmax(szTemp),"SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;")
while( SQL_MoreResults( hQuery ) )
{
// SELECT steamid, exp
// steamid is first, so its column number is 0
// then exp, so its column number is 1
SQL_ReadResult( hQuery, 0, szSteamID, charsmax( szSteamID ) );
iExp = SQL_ReadResult(hQuery, 1);

// print the player's top15 position and data
len += format(MenuBody[len], 511-len, "^n\r%d. %s %d Points", iPos, szSteamID, iExp)
// increase to next top15 position and go to next row
iPos++;
SQL_NextRow( hQuery );
}
} can you correct if is wrong please ?

Exolent[jNr]
11-03-2010, 15:17
Try testing before you post. That code won't even compile.

benjibau
11-04-2010, 15:01
Oh i think using SQL_ThreadQuery() in a func called with register_clcmd
and in the func i added this for choose steamid, exp from tutorial and after

new Temp[512]
format(Temp,charsmax(Temp),"SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;")
SQL_ThreadQuery(g_SqlTuple,"myfunc",Temp,Data,1)for exemple:
public ShowTop15(id)
{
//i must save stat for will haven't error into top15.
//
new Data[1]
Data[0] = id

new Temp[512]
format(Temp,charsmax(Temp),"SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;")
SQL_ThreadQuery(g_SqlTuple,"Sql_Top15",Temp,Data,1)
return PLUGIN_CONTINUE;
}

public Sql_Top15(FailState,Handle:hQuery,Error[],Errcode,Data[],DataSize)
{
new szSteamID[ 35 ], iExp;
new iPos = 1;

while( SQL_MoreResults( hQuery ) )
{
SQL_ReadResult( hQuery, 0, szSteamID, charsmax( szSteamID ) );
iExp = SQL_ReadResult(hQuery, 1);
server_print( "#%i: %s - %i", iPos, szSteamID, iExp );
iPos++;
SQL_NextRow( hQuery );
}
}edit: i tested with this but not work.

Exolent[jNr]
11-04-2010, 18:45
public ShowTop15(id)
{
//i must save stat for will haven't error into top15.
//
new Data[1]
Data[0] = id

// no need for a variable since it's not being formatted
/*new Temp[512]
format(Temp,charsmax(Temp),"SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;")
SQL_ThreadQuery(g_SqlTuple,"Sql_Top15",Temp,Data,1)*/
SQL_ThreadQuery(g_SqlTuple,"Sql_Top15","SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;",Data,1)
return PLUGIN_CONTINUE;
}

public Sql_Top15(FailState,Handle:hQuery,Error[],Errcode,Data[],DataSize)
{
// get player who is looking at top
new id = Data[ 0 ];

// check for errors
if( FailState == TQUERY_CONNECT_FAILED
|| FailState == TQUERY_QUERY_FAILED )
{
log_amx( "Error on top15 query (%i): %s", Errcode, Error );
console_print( id, "Error on top15 query (%i): %s", Errcode, Error );
return;
}

new szSteamID[ 35 ], iExp;
new iPos = 1;

while( SQL_MoreResults( hQuery ) )
{
SQL_ReadResult( hQuery, 0, szSteamID, charsmax( szSteamID ) );
iExp = SQL_ReadResult( hQuery, 1 );

// print to player, not server
//server_print( "#%i: %s - %i", iPos, szSteamID, iExp );
console_print( id, "#%i: %s - %i", iPos, szSteamID, iExp );

iPos++;
SQL_NextRow( hQuery );
}
}

Jacob
11-04-2010, 23:46
;1342133']public ShowTop15(id)
{
//i must save stat for will haven't error into top15.
//
new Data[1]
Data[0] = id

// no need for a variable since it's not being formatted
/*new Temp[512]
format(Temp,charsmax(Temp),"SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;")
SQL_ThreadQuery(g_SqlTuple,"Sql_Top15",Temp,Data,1)*/
SQL_ThreadQuery(g_SqlTuple,"Sql_Top15","SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;",Data,1)
return PLUGIN_CONTINUE;
}

public Sql_Top15(FailState,Handle:hQuery,Error[],Errcode,Data[],DataSize)
{
// get player who is looking at top
new id = Data[ 0 ];

// check for errors
if( FailState == TQUERY_CONNECT_FAILED
|| FailState == TQUERY_QUERY_FAILED )
{
log_amx( "Error on top15 query (%i): %s", Errcode, Error );
console_print( id, "Error on top15 query (%i): %s", Errcode, Error );
return;
}

new szSteamID[ 35 ], iExp;
new iPos = 1;

while( SQL_MoreResults( hQuery ) )
{
SQL_ReadResult( hQuery, 0, szSteamID, charsmax( szSteamID ) );
iExp = SQL_ReadResult( hQuery, 1 );

// print to player, not server
//server_print( "#%i: %s - %i", iPos, szSteamID, iExp );
console_print( id, "#%i: %s - %i", iPos, szSteamID, iExp );

iPos++;
SQL_NextRow( hQuery );
}
}
Thanks you,Exolent !

Exolent[jNr]
11-05-2010, 16:35
@grimvh

Your tutorial has errors that is spreading to users who copy/paste this for their own SQL saving.

Now our final code will look like this :


#include <amxmodx>
#include <sqlx>

#define PLUGIN "Tutorial"
#define VERSION "1.0"
#define AUTHOR "Grim"

// Ur Mysql Information
new Host[] = "hostname"
new User[] = "username"
new Pass[] = "password"
new Db[] = "database"


new Handle:g_SqlTuple
new g_Error[512]


new iExp[33]

// Pcvar's
new cKill
new cHeadshot
new cDeath

public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)

register_event("DeathMsg", "Event_DeathMsg", "a") // Register death event

// register the Pcvar's
cKill = register_cvar("exp_kill", "2")
cHeadshot = register_cvar("exp_headshot", "4")
cDeath = register_cvar("exp_death", "1")

set_task(1.0, "MySql_Init") // set a task to activate the mysql_init
}

public MySql_Init()
{
// we tell the API that this is the information we want to connect to,
// just not yet. basically it's like storing it in global variables
g_SqlTuple = SQL_MakeDbTuple(Host,User,Pass,Db)

// ok, we're ready to connect
new ErrorCode,Handle:SqlConnection = SQL_Connect(g_SqlTuple,ErrorCode,g_Error,char smax(g_Error))
if(SqlConnection == Empty_Handle)
// stop the plugin with an error message
set_fail_state(g_Error)

new Handle:Queries
// we must now prepare some random queries
Queries = SQL_PrepareQuery(SqlConnection,"CREATE TABLE IF NOT EXISTS tutorial (steamid varchar(32),exp INT(11))")

if(!SQL_Execute(Queries))
{
// if there were any problems
SQL_QueryError(Queries,g_Error,charsmax(g_Err or))
set_fail_state(g_Error)

}

// close the handle
SQL_FreeHandle(Queries)

// you free everything with SQL_FreeHandle
SQL_FreeHandle(SqlConnection)
}

public plugin_end()
{
// free the tuple - note that this does not close the connection,
// since it wasn't connected in the first place
SQL_FreeHandle(g_SqlTuple)
}

public Load_MySql(id)
{
new ErrorCode,Handle:SqlConnection = SQL_Connect(g_SqlTuple,ErrorCode,g_Error,char smax(g_Error))

if(g_SqlTuple == Empty_Handle)
set_fail_state(g_Error)

new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

new Data[1]
Data[0] = id

//we will now select from the table `tutorial` where the steamid match
format(szTemp,charsmax(szTemp),"SELECT * FROM `tutorial` WHERE (`tutorial`.`steamid` = '%s')", szSteamId)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)

SQL_FreeHandle(SqlConnection)
}

public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
{
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
}
else if(FailState == TQUERY_QUERY_FAILED)
{
log_amx("Load Query failed. [%d] %s", Errcode, Error)
}

new id
id = Data[0]

if(SQL_NumResults(Query) < 1)
{
//.if there are no results found

new szSteamId[32]
get_user_authid(id, szSteamId, charsmax(szSteamId)) // get user's steamid

// if its still pending we can't do anything with it
if (equal(szSteamId,"ID_PENDING"))
return PLUGIN_HANDLED

new szTemp[512]

// now we will insturt the values into our table.
format(szTemp,charsmax(szTemp),"INSERT INTO `tutorial` ( `steamid` , `exp`)VALUES ('%s','0');",szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
else
{
// if there are results found
iExp[id] = SQL_ReadResult(Query, 1)
}

return PLUGIN_HANDLED
}

public Save_MySql(id)
{
new szSteamId[32], szName[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

// Here we will update the user hes information in the database where the steamid matches.
format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i' WHERE `tutorial`.`steamid` = '%s';",iExp[id], szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}

public IgnoreHandle(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
SQL_FreeHandle(Query)

return PLUGIN_HANDLED
}

public client_putinserver(id)
{
Load_MySql(id)
}

public client_disconnect(id)
{
Save_MySql(id)
}

public Event_DeathMsg()
{
new iKiller = read_data(1) // read the data to get the killer and victim
new iVictim = read_data(2)

if(is_user_alive(iKiller)) // Check if the killer is alive in case he killed himself
{
if(read_data(3))
{
iExp[iKiller] += get_pcvar_num(cHeadshot) // Add the amount of the Pcvar to iExp
}
else
{
iExp[iKiller] += get_pcvar_num(cKill)
}
}
iExp[iVictim] -= get_pcvar_num(cDeath) // Decrease the amount of the Pcvar from iExp
}


The problem is here:

public Load_MySql(id)
{
new ErrorCode,Handle:SqlConnection = SQL_Connect(g_SqlTuple,ErrorCode,g_Error,char smax(g_Error))

if(g_SqlTuple == Empty_Handle)
set_fail_state(g_Error)

new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

new Data[1]
Data[0] = id

//we will now select from the table `tutorial` where the steamid match
format(szTemp,charsmax(szTemp),"SELECT * FROM `tutorial` WHERE (`tutorial`.`steamid` = '%s')", szSteamId)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)

SQL_FreeHandle(SqlConnection)
}


SQL_Connect() is for direct queries using SQL_PrepareQuery() and SQL_ThreadQuery() does not require a connection because it is set to a thread and connect/execute when it can.

It should be:

public Load_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

new Data[1]
Data[0] = id

//we will now select from the table `tutorial` where the steamid match
format(szTemp,charsmax(szTemp),"SELECT * FROM `tutorial` WHERE (`tutorial`.`steamid` = '%s')", szSteamId)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)
}

FEELiFE
11-07-2010, 10:51
;1341153']It's only a simple query. The rest is things you should know from the tutorial (or another SQL tutorial) which is reading the 15 top players.

SELECT steamid, exp FROM tutorial ORDER BY exp DESC LIMIT 15;

SELECT steamid, exp - Gets the steamid and exp from the table for each player
FROM tutorial - Gets data from tutorial table
ORDER BY exp DESC - Orders by the row's "exp" value descending so that the highest exp is first
LIMIT 15 - Limit 15 players in the results so if there are more than 15 they aren't used.

After executing this query, you would just need a simple loop:

Code:
new szSteamID[ 35 ], iExp;
new iPos = 1;

while( SQL_MoreResults( hQuery ) ) { // SELECT steamid, exp // steamid is first, so its column number is 0 // then exp, so its column number is 1 SQL_ReadResult( hQuery, 0, szSteamID, charsmax( szSteamID ) );
iExp = SQL_ReadResult( hQuery, 1 );

// print the player's top15 position and data server_print( "#%i: %s - %i", iPos, szSteamID, iExp );

// increase to next top15 position and go to next row iPos++; SQL_NextRow( hQuery );
}


Question about this: How can I select the exp where player name = current player name?

Exolent[jNr]
11-07-2010, 11:42
Question about this: How can I select the exp where player name = current player name?

Read the tutorial where it loads the player's xp.

grimvh2
11-15-2010, 12:36
@exo: yes thanks, I already knew that but it seems that I forgot to modify a part. Someone already pointed out that mistake before. Thanks for telling something slipped me.

grimvh2
11-15-2010, 12:37
Question about this: How can I select the exp where player name = current player name?

You need the steam id of the player if you want to get anything of that player.

k1nader
11-26-2010, 18:26
how to use set names 'utf8' ??

zeus
11-27-2010, 03:34
I have two errors with my plugin
L 11/26/2010 - 23:28:39: [MySQL] Invalid handle: 2
L 11/26/2010 - 23:28:39: [AMXX] Displaying debug trace (plugin "tbdm_xpmod.amxx")
L 11/26/2010 - 23:28:39: [AMXX] Run time error 10: native error (native "SQL_FreeHandle")
L 11/26/2010 - 23:28:39: [AMXX] [0] tbdm_xpmod.sma::plugin_end (line 94)
L 11/26/2010 - 23:29:05: Start of error session.
L 11/26/2010 - 23:29:05: Info (map "de_aztec_suncsm") (file "addons/amxmodx/logs/error_20101126.log")
L 11/26/2010 - 23:29:05: [MySQL] Invalid query handle: 1
L 11/26/2010 - 23:29:05: [AMXX] Displaying debug trace (plugin "tbdm_xpmod.amxx")
L 11/26/2010 - 23:29:05: [AMXX] Run time error 10: native error (native "SQL_ReadResult")
L 11/26/2010 - 23:29:05: [AMXX] [0] tbdm_xpmod.sma::Sql_Rank (line 235)


At line 94 I have
public plugin_end()
{
// free the tuple - note that this does not close the connection,
// since it wasn't connected in the first place
SQL_FreeHandle(g_SqlTuple)
}


And at line 235 I have
new icount = 0
icount=SQL_ReadResult(g_SqlTuple,0)
if(icount == 0)
icount = 1

Exolent[jNr]
11-27-2010, 04:00
You don't read a result from the tuple handle. You read it from the query handle.

zeus
11-27-2010, 04:23
And what do I have to modify?

Javivi
11-27-2010, 08:06
http://www.amxmodx.org/funcwiki.php?go=func&id=1105

zeus
11-27-2010, 08:28
I got it
icount=SQL_ReadResult(Query,0)

Exolent[jNr]
11-27-2010, 15:07
SQL_ReadResult( Query, COLUMN, VARIABLE, MAXLEN [only if variable is a string] )

Terrible description.

You even linked to the funcwiki and put a worse description.
SQL_ReadResult ( Handle:query, column, {Float,_}:... )

Passing no extra params - return int
Passing one extra param - return float in 1st extra arg
Passing two extra params - return string in 1st arg, max length in 2nd

Example:
new num = SQL_ReadResult(query, 0)
new Float:num2
new str[32]
SQL_ReadResult(query, 1, num2)
SQL_ReadResult(query, 2, str, 31)

Pass 2 params for an integer to be returned.
new value = SQL_ReadResult( query, column );
Pass 3 params for a float value to be passed byref.
new Float:value;
SQL_ReadResult( query, column, value );
Pass 4 params for a string value to be passed byref.
new value[32];
SQL_ReadResult( query, column, value, charsmax( value ) );

Vechta
12-11-2010, 04:44
This error always showing on console:


L 12/11/2010 - 10:43:27: [MySQL] Invalid handle: 0
L 12/11/2010 - 10:43:27: [AMXX] Displaying debug trace (plugin "mysql.amxx")
L 12/11/2010 - 10:43:27: [AMXX] Run time error 10: native error (native "SQL_FreeHandle")
L 12/11/2010 - 10:43:27: [AMXX] [0] mysql.sma::plugin_end (line 52)

Using original code.

Exolent[jNr]
12-11-2010, 17:31
Make sure that the handle is valid when created.

Vechta
12-12-2010, 02:16
How to check?

kamani
12-13-2010, 05:43
#include <amxmodx>
#include <sqlx>

#define PLUGIN "Tutorial"
#define VERSION "1.0"
#define AUTHOR "Grim"

new Host[] = "localhost"
new User[] = "root"
new Pass[] = "password"
new Db[] = "database"

new Handle:g_SqlTuple
new g_Error[512]
new iExp[33]
new cKill
new cHeadshot
new cDeath

public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
register_event("DeathMsg", "Event_DeathMsg", "a")
cKill = register_cvar("exp_kill", "2")
cHeadshot = register_cvar("exp_headshot", "4")
cDeath = register_cvar("exp_death", "1")
set_task(1.0, "MySql_Init")
}

public MySql_Init()
{
g_SqlTuple = SQL_MakeDbTuple(Host,User,Pass,Db)
new ErrorCode,Handle:SqlConnection = SQL_Connect(g_SqlTuple,ErrorCode,g_Error,char smax(g_Error))
if(SqlConnection == Empty_Handle)
set_fail_state(g_Error)
new Handle:Queries
Queries = SQL_PrepareQuery(SqlConnection,"CREATE TABLE IF NOT EXISTS tutorial (steamid varchar(32),exp INT(11))")

if(!SQL_Execute(Queries))
{
SQL_QueryError(Queries,g_Error,charsmax(g_Err or))
set_fail_state(g_Error)

}
SQL_FreeHandle(Queries)
SQL_FreeHandle(SqlConnection)
}

public plugin_end()
{
SQL_FreeHandle(g_SqlTuple)
}

public Load_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

new Data[1]
Data[0] = id
format(szTemp,charsmax(szTemp),"SELECT * FROM `tutorial` WHERE (`tutorial`.`steamid` = '%s')", szSteamId)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)
}

public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
{
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
}
else if(FailState == TQUERY_QUERY_FAILED)
{
log_amx("Load Query failed. [%d] %s", Errcode, Error)
}

new id
id = Data[0]

if(SQL_NumResults(Query) < 1)
{

new szSteamId[32]
get_user_authid(id, szSteamId, charsmax(szSteamId))

if (equal(szSteamId,"ID_PENDING"))
return PLUGIN_HANDLED

new szTemp[512]

format(szTemp,charsmax(szTemp),"INSERT INTO `tutorial` ( `steamid` , `exp`)VALUES ('%s','0');",szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
else
{
iExp[id] = SQL_ReadResult(Query, 1)
}

return PLUGIN_HANDLED
}

public Save_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i' WHERE `tutorial`.`steamid` = '%s';",iExp[id], szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}

public IgnoreHandle(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
SQL_FreeHandle(Query)

return PLUGIN_HANDLED
}

public client_putinserver(id)
{
Load_MySql(id)
}

public client_disconnect(id)
{
Save_MySql(id)
}

public Event_DeathMsg()
{
new iKiller = read_data(1) // read the data to get the killer and victim
new iVictim = read_data(2)

if(is_user_alive(iKiller)) // Check if the killer is alive in case he killed himself
{
if(read_data(3))
{
iExp[iKiller] += get_pcvar_num(cHeadshot) // Add the amount of the Pcvar to iExp
}
else
{
iExp[iKiller] += get_pcvar_num(cKill)
}
}
iExp[iVictim] -= get_pcvar_num(cDeath) // Decrease the amount of the Pcvar from iExp
}

1. I changed mysql connect info (host,db,user adn pass).
2. I put this code in blabla.sma file.
3. Compiling in amxx :)
4. put in server plugins folder, change plugins.cfg file (added: blabla.amxx)

but script doesn't work. mh don't create in mysql tutorial table..
connect work fine. maybe i just have wrong amxmodx version?

fearAR
12-13-2010, 10:06
Excellent, I understood it very good. :D I will use it in a future because I am doing and improving my Mod, Great Thanks!

Vechta
12-14-2010, 12:14
How to check?

No one?

grimvh2
04-12-2011, 11:32
No one?

This happends when your table does'nt exists.

grimvh2
04-12-2011, 11:33
#include <amxmodx>
#include <sqlx>

#define PLUGIN "Tutorial"
#define VERSION "1.0"
#define AUTHOR "Grim"

new Host[] = "localhost"
new User[] = "root"
new Pass[] = "password"
new Db[] = "database"

new Handle:g_SqlTuple
new g_Error[512]
new iExp[33]
new cKill
new cHeadshot
new cDeath

public plugin_init()
{
register_plugin(PLUGIN, VERSION, AUTHOR)
register_event("DeathMsg", "Event_DeathMsg", "a")
cKill = register_cvar("exp_kill", "2")
cHeadshot = register_cvar("exp_headshot", "4")
cDeath = register_cvar("exp_death", "1")
set_task(1.0, "MySql_Init")
}

public MySql_Init()
{
g_SqlTuple = SQL_MakeDbTuple(Host,User,Pass,Db)
new ErrorCode,Handle:SqlConnection = SQL_Connect(g_SqlTuple,ErrorCode,g_Error,char smax(g_Error))
if(SqlConnection == Empty_Handle)
set_fail_state(g_Error)
new Handle:Queries
Queries = SQL_PrepareQuery(SqlConnection,"CREATE TABLE IF NOT EXISTS tutorial (steamid varchar(32),exp INT(11))")

if(!SQL_Execute(Queries))
{
SQL_QueryError(Queries,g_Error,charsmax(g_Err or))
set_fail_state(g_Error)

}
SQL_FreeHandle(Queries)
SQL_FreeHandle(SqlConnection)
}

public plugin_end()
{
SQL_FreeHandle(g_SqlTuple)
}

public Load_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

new Data[1]
Data[0] = id
format(szTemp,charsmax(szTemp),"SELECT * FROM `tutorial` WHERE (`tutorial`.`steamid` = '%s')", szSteamId)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)
}

public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
{
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
}
else if(FailState == TQUERY_QUERY_FAILED)
{
log_amx("Load Query failed. [%d] %s", Errcode, Error)
}

new id
id = Data[0]

if(SQL_NumResults(Query) < 1)
{

new szSteamId[32]
get_user_authid(id, szSteamId, charsmax(szSteamId))

if (equal(szSteamId,"ID_PENDING"))
return PLUGIN_HANDLED

new szTemp[512]

format(szTemp,charsmax(szTemp),"INSERT INTO `tutorial` ( `steamid` , `exp`)VALUES ('%s','0');",szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
else
{
iExp[id] = SQL_ReadResult(Query, 1)
}

return PLUGIN_HANDLED
}

public Save_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i' WHERE `tutorial`.`steamid` = '%s';",iExp[id], szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}

public IgnoreHandle(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
SQL_FreeHandle(Query)

return PLUGIN_HANDLED
}

public client_putinserver(id)
{
Load_MySql(id)
}

public client_disconnect(id)
{
Save_MySql(id)
}

public Event_DeathMsg()
{
new iKiller = read_data(1) // read the data to get the killer and victim
new iVictim = read_data(2)

if(is_user_alive(iKiller)) // Check if the killer is alive in case he killed himself
{
if(read_data(3))
{
iExp[iKiller] += get_pcvar_num(cHeadshot) // Add the amount of the Pcvar to iExp
}
else
{
iExp[iKiller] += get_pcvar_num(cKill)
}
}
iExp[iVictim] -= get_pcvar_num(cDeath) // Decrease the amount of the Pcvar from iExp
} 1. I changed mysql connect info (host,db,user adn pass).
2. I put this code in blabla.sma file.
3. Compiling in amxx :)
4. put in server plugins folder, change plugins.cfg file (added: blabla.amxx)

but script doesn't work. mh don't create in mysql tutorial table..
connect work fine. maybe i just have wrong amxmodx version?

Error Log?

Could be wrong modules enabled or the connection from your server to your mysql db

(srr for double post)


Added new examples :
-How to store floats.
-How to delete a row.

If you want more examples about things you want to see in this tutorial, feel free to pm me or to post something here.

Kia
05-07-2011, 05:24
[AMXX] Run time error 10: native error (native "SQL_ThreadQuery")
I get this message in my console when I try to ask for my rank and how much points I have.

Same with:

Load_MySql (line 94)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)
and


Save_MySql (line 144)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
Why it doesn't work? I use local Database.

Error is always:


Invalid info tuple handle: 0

grimvh2
05-11-2011, 16:22
There is no correct connection to your database? Are you're tables created? I dont think so.

Kia
05-12-2011, 10:08
I tried to connect with a External Database Checker, it says it works.
And no there isn't any table.

grimvh2
05-17-2011, 14:10
ddo amx_modules to see if all ur modules are running correctly

EpicMonkey
06-05-2012, 04:07
whats with the szname

public Save_MySql(id)
{
new szSteamId[32], szName[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

// Here we will update the user hes information in the database where the steamid matches.
format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `exp` = '%i' WHERE `tutorial`.`steamid` = '%s';",iExp[id], szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}

your not using it...

quark
06-11-2012, 17:05
This is supposed to be to save playername in-game. But he doesn't used it :]

EpicMonkey
06-12-2012, 02:12
This is supposed to be to save playername in-game. But he doesn't used it :]

thats exactly what am pointing out :3

yamin
06-16-2012, 08:03
good job

V I R U S
09-18-2012, 20:12
Thanks for the great tut, but there is still a leak in whole AMXX SQL system.

For a day, i've noticed my website fallig down with an error "TOO MANY CONNECTIONS". No problem to raise up the number of connections for MySQL server, but i've noticed that every one CStrike + AMXX gameservers, makes over 8 SQL connections.

Somehow, that suxx. Mby there is some way to create only one connection to database and use that one for every plugin?

Alka
09-19-2012, 07:50
Thanks for the great tut, but there is still a leak in whole AMXX SQL system.

For a day, i've noticed my website fallig down with an error "TOO MANY CONNECTIONS". No problem to raise up the number of connections for MySQL server, but i've noticed that every one CStrike + AMXX gameservers, makes over 8 SQL connections.

Somehow, that suxx. Mby there is some way to create only one connection to database and use that one for every plugin?
The only thing that i can think now, it's to register a native and call it every time when you want to insert/retrieve some data into/from a DB. If a connection it's already active, wait to finish then close it and reopen a new one.

4444leozinho
09-24-2012, 16:00
I was wondering how do to put a function for admin to load the information from the steam that he enter.

skz
07-29-2014, 13:00
forget it

aron9forever
09-11-2014, 07:35
The only thing that i can think now, it's to register a native and call it every time when you want to insert/retrieve some data into/from a DB. If a connection it's already active, wait to finish then close it and reopen a new one.

I also had this problem and I found out it's a problem in the tutorial:
public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
{
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
}
else if(FailState == TQUERY_QUERY_FAILED)
{
log_amx("Load Query failed. [%d] %s", Errcode, Error)
}

new id
id = Data[0]

if(SQL_NumResults(Query) < 1)
{
//.if there are no results found

new szSteamId[32]
get_user_authid(id, szSteamId, charsmax(szSteamId)) // get user's steamid

// if its still pending we can't do anything with it
if (equal(szSteamId,"ID_PENDING"))
return PLUGIN_HANDLED

new szTemp[512]

// now we will insturt the values into our table.
format(szTemp,charsmax(szTemp),"INSERT INTO `tutorial` ( `steamid` , `exp`)VALUES ('%s','0');",szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
else
{
// if there are results found
iExp[id] = SQL_ReadResult(Query, 1)
}

return PLUGIN_HANDLED
}


->>


public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
{
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
}
else if(FailState == TQUERY_QUERY_FAILED)
{
log_amx("Load Query failed. [%d] %s", Errcode, Error)
}

new id
id = Data[0]

if(SQL_NumResults(Query) < 1)
{
//.if there are no results found

new szSteamId[32]
get_user_authid(id, szSteamId, charsmax(szSteamId)) // get user's steamid

// if its still pending we can't do anything with it
if (equal(szSteamId,"ID_PENDING"))
return PLUGIN_HANDLED

new szTemp[512]

// now we will insturt the values into our table.
format(szTemp,charsmax(szTemp),"INSERT INTO `tutorial` ( `steamid` , `exp`)VALUES ('%s','0');",szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
else
{
// if there are results found
iExp[id] = SQL_ReadResult(Query, 1)
}
SQL_FreeHandle(Query)
return PLUGIN_HANDLED
}

Notice the extra SQL_FreeHandle(Query) before returning PLUGIN_HANDLED
new connections are created for every user that logs in and is already registered because the original "loading" query never actually calls "IgnoreHandle"

skz
12-22-2014, 11:02
This tutorial have so much bugs, I dont know why any people didnt noticed before. With this way to save in mysql, I guarantee you that you ll have a lot of lost data.

Jhob94
12-22-2014, 11:19
This tutorial have so much bugs, I dont know why any people didnt noticed before. With this way to save in mysql, I guarantee you that you ll have a lot of lost data.

Make a new tutorial :up:

skz
12-22-2014, 11:27
Make a new tutorial :up:

I'm exploring the sqlx module, I'm not a expert on it, but when I finish the way I'm saving I'll make a tutorial about it

This tutorial just gave me cancer when he loads the player data in client_putinserver...

Jhob94
12-22-2014, 11:31
This tutorial just gave me cancer when he loads the player data in client_putinserver...

Idk anything about mysql, but i don't see why is that a problem.

skz
12-22-2014, 11:38
It should be on client authorized to be safer, and he inserts into the table the players stats if they are new right on it, and it should be when the players make some xp

Jhob94
12-22-2014, 11:44
It should be on client authorized to be more saffer, and he inserts into the table the players stats if they are new right on it, and it should be when the players make some xp

To be honest authorized doesn't means more safe since authorized is when the player is valid (example, steam server kicking a non-steam user). Putinserver is called after authorized so i think there is nothing wrong with it (connect->authorized->putinserver).

About saving xp, i dont see why that is wrong.

guipatinador
12-22-2014, 11:54
Normally the order is,
client_connect -> client_authorized -> client_putinserver

But in some (rare) situations client_authorized is called after,
client_connect -> client_putinserver -> client_authorized

Note that there is no default order and client_connect is always the first.

============

Keep in mind that client_authorized sometimes is called to fast, right after plugin_init. This could be dangerous if you create the global mysql handle in plugin_init or plugin_cfg.


g_SqlTuple = SQL_MakeDbTuple( szHost, szUser, szPass, szDB )


Thats why some coders use client_putinserver.

skz
12-22-2014, 12:02
To be honest authorized doesn't means more safe since authorized is when the player is valid (example, steam server kicking a non-steam user). Putinserver is called after authorized so i think there is nothing wrong with it (connect->authorized->putinserver).

About saving xp, i dont see why that is wrong.

I cant be specific in english but I guarantee you that if the player stats were only saved when they make some xo or another thing that you're trying to save is more safe too.

Keep in mind that client_authorized sometimes is called to fast, right after plugin_init. This could be dangerous if you create the global mysql handle in plugin_init or plugin_cfg.


g_SqlTuple = SQL_MakeDbTuple( szHost, szUser, szPass, szDB )


Thats why some coders use client_putinserver.

I saw in your plugin that you set a task after client authorized, thats the reason why you do that?

guipatinador
12-22-2014, 16:59
I saw in your plugin that you set a task after client authorized, thats the reason why you do that?

Yes.

Lycode
12-22-2014, 23:26
Normally the order is,
client_connect -> client_authorized -> client_putinserver

But in some (rare) situations client_authorized is called after,
client_connect -> client_putinserver -> client_authorized

Note that there is no default order and client_connect is always the first.

============

Keep in mind that client_authorized sometimes is called to fast, right after plugin_init. This could be dangerous if you create the global mysql handle in plugin_init or plugin_cfg.


g_SqlTuple = SQL_MakeDbTuple( szHost, szUser, szPass, szDB )


Thats why some coders use client_putinserver.

1- Does this mean if we use client_putinserver it wouldn't be necessary to use a task after client authorized like you do?

2- Where else can we create the global handle that would remove the risk of client_authorized being called too fast causing problems?

3- Is a task of 4.0 seconds in client_authorized really needed? or would something like 0.1 work just as good?

guipatinador
12-23-2014, 06:06
1- Does this mean if we use client_putinserver it wouldn't be necessary to use a task after client authorized like you do?
client_putinserver is always a risk to get the player authid. Use client_authorized for safety.

2- Where else can we create the global handle that would remove the risk of client_authorized being called too fast causing problems?You can't. Use plugin_init or plugin_cfg.

3- Is a task of 4.0 seconds in client_authorized really needed? or would something like 0.1 work just as good?It is required. I'm not sure the ammount of time. For example amxbans uses 2 seconds,


public client_authorized(id) {
//fix for the invalid tuple error at mapchange, only a fast fix now
if(g_SqlX==Empty_Handle) {
set_task(2.0,"client_authorized",id)
return PLUGIN_HANDLED
}
// code...
}

Jhob94
12-23-2014, 06:22
public client_authorized(id) {
//fix for the invalid tuple error at mapchange, only a fast fix now
if(g_SqlX==Empty_Handle) {
set_task(2.0,"client_authorized",id)
return PLUGIN_HANDLED
}
// code...
}


Checking the empty handle is definitly better than create unnecessary tasks. Now the server is happy and the data will be secure :3

guipatinador
12-23-2014, 06:38
Its not "definitly better".

If the plugin cannot connect to database (example: database offline, wrong password, etc), client_authorized is called each 2 seconds because g_SqlX will be always Empty_Handle.

Jhob94
12-23-2014, 06:46
Its not "definitly better".

If the plugin cannot connect to database (example: database offline, wrong password, etc), client_authorized is called each 2 seconds because g_SqlX will be always Empty_Handle.

That's because you are noob :3
Set task time for 5.0 to another function that isn't client_authorized. Didn't even noticed you looped the client authorized lol, thats so wrong :grrr:

guipatinador
12-23-2014, 07:49
Didn't even noticed you looped the client authorized lol, thats so wrong :grrr:

Thats an example of amx bans, https://code.google.com/p/plukom-amxx-plugins/source/browse/trunk/scripting/amxbans_main.sma?r=3&spec=svn17#239

Obviously looping client_authorized isn't a good idea.

Jhob94
12-23-2014, 17:46
This should be ok:
#define TASK_SQL 432531
#define MAX_PLAYERS 32

new g_Steam[MAX_PLAYERS + 1][35]

public client_authorized(Id)
{
get_user_authid(Id, g_Steam[Id], charsmax(g_Steam)) // Lets get SteamID just once. Safe for client_disconnect saving

if(g_SqlX == Empty_Handle)
set_task(5.0,"LoadShit",Id + TASK_SQL)

else
LoadShit(Id + TASK_SQL)
}

public LoadShit(Id)
{
Id-= TASK_SQL

// Code
}

guipatinador
12-23-2014, 18:53
Its okay.

But I would do the following only to prevent offline databases,



public LoadShit(Id)
{
if(g_SqlX == Empty_Handle)
{
// something is wrong with the connection (maybe offline / wrong ip / user / password)
return
}

Id-= TASK_SQL

// Code
}

=========================

charsmax(g_Steam))
If g_Steam it's a bidimensional array it shoud be,

charsmax(g_Steam[]))

Jhob94
12-23-2014, 20:25
Its okay.

But I would do the following only to prevent offline databases,



public LoadShit(Id)
{
if(g_SqlX == Empty_Handle)
{
// something is wrong with the connection (maybe offline / wrong ip / user / password)
return
}

Id-= TASK_SQL

// Code
}

Or just using a variable and put it positive in plugin_init after know if we got an Empty_Handle and work with the variable. Anyway, that's something i don't need right now. I am very new into sql so i have some doubts.

Should i regist like this:
SQL_ThreadQuery(g_Ranking, "EmptyFunction", "CREATE TABLE IF NOT EXISTS Rankings (Steam TEXT, Level NUMERIC, Kills NUMERIC, Deaths NUMERIC);")

Or should i create one table for each thing?

The reason why i am asking this is because i am currently using this to get data:
formatex(Query, charsmax(Query), "SELECT Kills FROM Rankings WHERE Steam = '%s';", g_Steam[Id])
SQL_ThreadQuery(g_Ranking, "Query_Handler", Query, Data, charsmax(Data))


And in Query_Handler: SQL_ReadResult(pQuery, 0)

With this could i get all the data and parse it or something? Or will i have to do 3 queries?

And last question. Should i update things everytime the values change or loop a task to save it from x in x time?

guipatinador
12-23-2014, 20:47
Should i regist like this:
SQL_ThreadQuery(g_Ranking, "EmptyFunction", "CREATE TABLE IF NOT EXISTS Rankings (Steam TEXT, Level NUMERIC, Kills NUMERIC, Deaths NUMERIC);")

Or should i create one table for each thing?


Your "CREATE TABLE..." query is ok but personally I would change two things.

1 - Replace TEXT by VARCHAR.
"CREATE TABLE IF NOT EXISTS Rankings (Steam VARCHAR(35), Level INT, Kills INT, Deaths INT)"

2 - Use a non-threaded query for "CREATE TABLE...", since its called in plugin_init / plugin_cfg once and will not affect the gameplay (for the rest, use always threaded querys).

new Handle:Queries = SQL_PrepareQuery(g_Ranking, "CREATE TABLE IF NOT EXISTS Rankings (Steam VARCHAR(35), Level INT, Kills INT, Deaths INT)")

if(!SQL_Execute( Queries ))
{
// optionally you can turn off the plugin
SQL_QueryError(Queries, g_Error, charsmax(g_Error))
set_fail_state(g_Error)
}

SQL_FreeHandle(Queries)

The reason why i am asking this is because i am currently using this to get data:
formatex(Query, charsmax(Query), "SELECT Kills FROM Rankings WHERE Steam = '%s';", g_Steam[Id])
SQL_ThreadQuery(g_Ranking, "Query_Handler", Query, Data, charsmax(Data))


And in Query_Handler: SQL_ReadResult(pQuery, 0)
With this could i get all the data and parse it or something? Or will i have to do 3 queries?Its simple with a single query. Just change "SELECT Kills" to "SELECT *" to select all the fields.
Optionally you can select the fields you want,


formatex(Query, charsmax(Query), "SELECT Level, Kills Deaths FROM Rankings WHERE Steam = '%s';", g_Steam[Id])
SQL_ThreadQuery(g_Ranking, "Query_Handler", Query, Data, charsmax(Data))
And then in Query_Handler,


iLevel = SQL_ReadResult(pQuery, 0) // first field
iKills = SQL_ReadResult(pQuery, 1) // second field
iDeaths = SQL_ReadResult(pQuery, 2) // third field

And last question. Should i update things everytime the values change or loop a task to save it from x in x time?Updating values every time they change may generate a lot of traffic between the server and the database. In most situations the most recommendable is saving at client_disconnect.

Unfortunately when the map change client_disconnect is called for everyone so it may cause a lot of traffic too.

frOOgy
01-31-2015, 06:01
Can anyone make me an example plugin for rank using MySQL. Save Rank, kills, deaths. When player say /rank then show message: "Your rank is %d with %d kills and %d deaths". THXX

skz
02-05-2015, 21:02
Grim, In this tutorial, you should save the steam ID when a player join the server and save it as a global variable, because it isn't safe to detected the steam id of someone when they disconnect (as you do on this tutorial)

frOOgy
02-06-2015, 03:52
Bro, I want to know how to save rank position, kills, deaths using mysql to name, not steam id. thxx

skz
02-06-2015, 14:03
I didn't answer to your request frosty, but btw, why don't you use steam id for saving instead of the name?

Jhob94
02-06-2015, 14:07
I didn't answer to your request frosty, but btw, why don't you use steam id for saving instead of the name?

Guess why. Everyone that requests to save by name instead of steam id, requests it for only one single reason: They want to be banned :crab:

frOOgy
02-07-2015, 05:45
nvm... I just want to save using MySQL. Can anyone make me an example plugin plzz ???

skz
02-07-2015, 09:08
froogy, you have an example of a plugin that saves data in this thread

frOOgy
02-07-2015, 15:00
So, can anyone edit BaD CopY's plugin and change nVault to MySQL ?

#include <amxmodx>
#include <amxmisc>
#include <colorchat>
#include <csstats>
#include <nvault>
#include <csx>

#define PLUGIN "Rank System"
#define VERSION "1.0"
#define AUTHOR "BaD CopY"

new gTime[33]
new gVault


public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)

register_clcmd("say /rank", "cmd_rank")
register_clcmd("say rank", "cmd_rank")

register_clcmd("team_say /rank", "cmd_rank")
register_clcmd("team_say rank", "cmd_rank")

gVault = nvault_open("TIME")
}

public cmd_rank(id)
{
new stats[8], body[8], szName[33];

get_user_name(id, szName, 32)

new rank_pos = get_user_stats(id, stats, body)
new rank_max = get_statsnum()

new iTimeMins = gTime[id] / 60

static Deaths = 0, Kills = 0, Float:Ratio = 0.0

Deaths = stats[1], Kills = stats[0], Ratio = Deaths == 0 ? (float(Kills)) : (float(Kills) / float(Deaths))

ColorChat(0, GREEN, "^4[Rank]^3 %s^1's rank is ^4%d^1 of ^4%d ^3-- ^1KpD: ^4%.2f^1 [Online: %i m]", szName, rank_pos, rank_max, Ratio, iTimeMins)

return PLUGIN_CONTINUE;
}

public client_authorized(id)
{
loaddata(id)
}

public client_disconnect(id)
{
gTime[id] += get_user_time(id)
savedata(id)
}

stock savedata(id)
{
new AuthId[65]
get_user_authid(id, AuthId, charsmax(AuthId))

new VaultKey[64], VaultData[256]
format(VaultKey, 63, "%s-TIME", AuthId)
format(VaultData, 254, "%i", gTime[id])

nvault_set(gVault, VaultKey, VaultData)

return PLUGIN_CONTINUE
}

stock loaddata(id)
{
new AuthID[35]
get_user_authid(id,AuthID,charsmax(AuthID ))
new vaultkey[64],vaultdata[256]

format(vaultkey,63,"%s-TIME" ,AuthID)
format(vaultdata,255,"%i",gTime[id])

nvault_get(gVault,vaultkey,vaultdata,charsmax (vaultdata))

new Time[33]
parse(vaultdata, Time, charsmax(Time))

gTime[id] = str_to_num(Time)
}

Eagle07
02-07-2015, 16:46
So, can anyone edit BaD CopY's plugin and change nVault to MySQL ?

#include <amxmodx>
#include <amxmisc>
#include <colorchat>
#include <csstats>
#include <nvault>
#include <csx>

#define PLUGIN "Rank System"
#define VERSION "1.0"
#define AUTHOR "BaD CopY"

new gTime[33]
new gVault


public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)

register_clcmd("say /rank", "cmd_rank")
register_clcmd("say rank", "cmd_rank")

register_clcmd("team_say /rank", "cmd_rank")
register_clcmd("team_say rank", "cmd_rank")

gVault = nvault_open("TIME")
}

public cmd_rank(id)
{
new stats[8], body[8], szName[33];

get_user_name(id, szName, 32)

new rank_pos = get_user_stats(id, stats, body)
new rank_max = get_statsnum()

new iTimeMins = gTime[id] / 60

static Deaths = 0, Kills = 0, Float:Ratio = 0.0

Deaths = stats[1], Kills = stats[0], Ratio = Deaths == 0 ? (float(Kills)) : (float(Kills) / float(Deaths))

ColorChat(0, GREEN, "^4[Rank]^3 %s^1's rank is ^4%d^1 of ^4%d ^3-- ^1KpD: ^4%.2f^1 [Online: %i m]", szName, rank_pos, rank_max, Ratio, iTimeMins)

return PLUGIN_CONTINUE;
}

public client_authorized(id)
{
loaddata(id)
}

public client_disconnect(id)
{
gTime[id] += get_user_time(id)
savedata(id)
}

stock savedata(id)
{
new AuthId[65]
get_user_authid(id, AuthId, charsmax(AuthId))

new VaultKey[64], VaultData[256]
format(VaultKey, 63, "%s-TIME", AuthId)
format(VaultData, 254, "%i", gTime[id])

nvault_set(gVault, VaultKey, VaultData)

return PLUGIN_CONTINUE
}

stock loaddata(id)
{
new AuthID[35]
get_user_authid(id,AuthID,charsmax(AuthID ))
new vaultkey[64],vaultdata[256]

format(vaultkey,63,"%s-TIME" ,AuthID)
format(vaultdata,255,"%i",gTime[id])

nvault_get(gVault,vaultkey,vaultdata,charsmax (vaultdata))

new Time[33]
parse(vaultdata, Time, charsmax(Time))

gTime[id] = str_to_num(Time)
}

try

#include <amxmodx>
#include <amxmisc>
#include <colorchat>
#include <csstats>
#include <csx>
#include <sqlx>

#define PLUGIN "Rank System"
#define VERSION "1.0"
#define AUTHOR "BaD CopY"

// Ur Mysql Information
new Host[] = "hostname"
new User[] = "username"
new Pass[] = "password"
new Db[] = "database"

new gTime[33]
new Handle:g_SqlTuple
new g_Error[512]

public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)

register_clcmd("say /rank", "cmd_rank")
register_clcmd("say rank", "cmd_rank")

register_clcmd("team_say /rank", "cmd_rank")
register_clcmd("team_say rank", "cmd_rank")

}

public MySql_Init()
{
g_SqlTuple = SQL_MakeDbTuple(Host,User,Pass,Db)

new ErrorCode,Handle:SqlConnection = SQL_Connect(g_SqlTuple,ErrorCode,g_Error,char smax(g_Error))
if(SqlConnection == Empty_Handle)
set_fail_state(g_Error)

new Handle:Queries
Queries = SQL_PrepareQuery(SqlConnection,"CREATE TABLE IF NOT EXISTS tutorial (steamid varchar(32),time INT(11))")

if(!SQL_Execute(Queries))
{
SQL_QueryError(Queries,g_Error,charsmax(g_Err or))
set_fail_state(g_Error)

}

SQL_FreeHandle(Queries)

SQL_FreeHandle(SqlConnection)
}

public cmd_rank(id)
{
new stats[8], body[8], szName[33];

get_user_name(id, szName, 32)

new rank_pos = get_user_stats(id, stats, body)
new rank_max = get_statsnum()

new iTimeMins = gTime[id] / 60

static Deaths = 0, Kills = 0, Float:Ratio = 0.0

Deaths = stats[1], Kills = stats[0], Ratio = Deaths == 0 ? (float(Kills)) : (float(Kills) / float(Deaths))

ColorChat(0, GREEN, "^4[Rank]^3 %s^1's rank is ^4%d^1 of ^4%d ^3-- ^1KpD: ^4%.2f^1 [Online: %i m]", szName, rank_pos, rank_max, Ratio, iTimeMins)

return PLUGIN_CONTINUE;
}

public client_authorized(id)
{
Load_MySql(id)
}

public client_disconnect(id)
{
gTime[id] += get_user_time(id)
Save_MySql(id)
}


public Load_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

new Data[1]
Data[0] = id

format(szTemp,charsmax(szTemp),"SELECT * FROM `tutorial` WHERE (`tutorial`.`steamid` = '%s')", szSteamId)
SQL_ThreadQuery(g_SqlTuple,"register_client",szTemp,Data,1)
}

public Save_MySql(id)
{
new szSteamId[32], szTemp[512]
get_user_authid(id, szSteamId, charsmax(szSteamId))

format(szTemp,charsmax(szTemp),"UPDATE `tutorial` SET `time` = '%i' WHERE `tutorial`.`steamid` = '%s';",gTime[id], szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}

public register_client(FailState,Handle:Query,Error[],Errcode,Data[],DataSize)
{
if(FailState == TQUERY_CONNECT_FAILED)
{
log_amx("Load - Could not connect to SQL database. [%d] %s", Errcode, Error)
}
else if(FailState == TQUERY_QUERY_FAILED)
{
log_amx("Load Query failed. [%d] %s", Errcode, Error)
}

new id
id = Data[0]

if(SQL_NumResults(Query) < 1)
{

new szSteamId[32]
get_user_authid(id, szSteamId, charsmax(szSteamId))

if (equal(szSteamId,"ID_PENDING"))
return PLUGIN_HANDLED

new szTemp[512]

format(szTemp,charsmax(szTemp),"INSERT INTO `tutorial` ( `steamid` , `timef`)VALUES ('%s','0');",szSteamId)
SQL_ThreadQuery(g_SqlTuple,"IgnoreHandle",szTemp)
}
else
{
gTime[id] = SQL_ReadResult(Query, 1)
}

return PLUGIN_HANDLED
}




public plugin_end()
{

SQL_FreeHandle(g_SqlTuple)
}

frOOgy
02-07-2015, 17:59
Can you fix the code and make to show hours and minuts online, not just minutes ? :D

And can you say me for what we need this part of the code:

new Host[] = "hostname"
new User[] = "username"
new Pass[] = "password"
new Db[] = "database"

thxx for replay bro :)

Jhob94
02-11-2015, 05:24
Can you fix the code and make to show hours and minuts online, not just minutes ? :D

And can you say me for what we need this part of the code:

new Host[] = "hostname"
new User[] = "username"
new Pass[] = "password"
new Db[] = "database"

thxx for replay bro :)

Are you sure that you want a sql and not nvault/fvault plugin?


Anyway, i need help about two thing. How do i get a string?
Also, lets say that i have in db the same steam id 2 times, but different values. How do i get those variables?

frOOgy
02-11-2015, 11:53
Because MySQL is most popular vault system and I want to learn how to use it :)

Jhob94
02-11-2015, 11:56
Because MySQL is most popular vault system and I want to learn how to use it :)

Then, first get a database before request the plugin...

skz
02-11-2015, 13:23
Anyway, i need help about two thing. How do i get a string?
Also, lets say that i have in db the same steam id 2 times, but different values. How do i get those variables?

Which string?
Why would you have the same steam id twice on your database?

frOOgy
02-12-2015, 14:57
nevermind. did I need this part of the code:

[php]new Host[] = "hostname"
new User[] = "username"
new Pass[] = "password"
new Db[] = "database" [/pph]

Jhob94
02-12-2015, 15:19
nevermind. did I need this part of the code:

[php]new Host[] = "hostname"
new User[] = "username"
new Pass[] = "password"
new Db[] = "database" [/pph]

yes you need it. you need a database and put the information about database.
Anyway, sql isnt the best. It has advantages but also has disadvantages. The biggest advantage is that you will be able to make php scripts and show saved stuff in a website. If you dont need that, go use nvault or fvault because they are better.

frOOgy
02-12-2015, 17:37
ok, thxx for help...

t/c

Ant1Lamer
05-31-2022, 13:56
Can you tell me how to save kills in sql and how to load this kills ? and how to remove after a given period of time.

P.S: This plugin i cant to use "#include <csstats>" just to need to make "x" kills and after make this "x" kills they remove a given period of time

Bugsy
06-17-2022, 11:07
Do you just need the SQL or are you asking for code? For something simple like this, you could always use nVault, which includes the prune function to delete stuff after X time.