Bueno.. primero antes que nada yo veo como este tutorial para gente con conocimiento #Medio sobre Amxx pero seguro para muchos lo vean como #Alto conocimiento por lo tanto hay cosas que no explico que ya doy por sentado como conocidas dado que si estas acá ya sabrás como hacer un guardado sino este tutorial es mucho para vos.
El plugin base que les voy a mostrar utiliza el ServerManager así que si ves algo referido a este ya sabes donde buscar información
sm_GuardadoCore
Veamos mas detalladamente.
Acá no hay mucho que explicar.. el ID del registro es el ID que se crea cuando el usuario se registra por primera vez y con el cual vamos a identificar en el resto de los guardados
Acá le avisamos a nuestro plugin que otro plugin va a guardar/cargar datos
La anotación lo dice todo
Acá lo que hacemos es avisarle a nuestro plugin principal que otro plugin ya cargo datos. Si nuestro plugin principal cargo la misma cantidad de datos de las que están registradas logueamos al usuario
Muy importante esto.. Obtenemos las consultas con los datos para GUARDAR; si, el guardado de datos se hace TODO JUNTO así no se satura la DB; hay que tener en cuenta que tenemos un limite en este caso de 1023 caracteres para almacenar así que TENE EN CUENTA DE NO PASAR EL LIMITE y de ser necesario AUMENTALO
Esta forward le avisa a los otros plugins que un usuario tiene que cargar/crear los datos.. para diferenciarlo mandamos de ultimo parámetro 1/0 dependiendo si es la primera vez que se loguea o no. Ademas de esto manda el ID de registro y el ID del usuario.
Después de ver todo esto poray te estas pensando.. Para que voy a hacer esta locura si poniéndolo todo en un solo plugin voy a hacer lo mismo.. la diferencia esta en que es mas como ( A mi gusto ) tenerlo separado que andar buscando entre MUCHAS lineas de código y en lo personal es mucho mas fácil agregar/quitar datos a guardar/cargar.
Después de estas palabras... seguimos con el resto.
En el plugin esta todo explicado
sm_ExpMode
Si tienen alguna duda solo díganme y serán respondidas.
PD: No probé ningún plugin; puede q me dejara algún cabo suelto
El plugin base que les voy a mostrar utiliza el ServerManager así que si ves algo referido a este ya sabes donde buscar información
sm_GuardadoCore
Código PHP:
#include <amxmodx>
#include <servermanager>
#include <sqlx>
#define PLUGIN "SM Guardado Core"
#define VERSION "1.0"
#define AUTHOR "[R]ak"
/*************************************************************************************/
/*********************************************/
#define SQL_HOST "127.0.0.1" /*********************************************/
#define SQL_USER "root" /*********************************************/
#define SQL_PASS "" /*********************************************/
#define SQL_DATEBASE "test" /*********************************************/
/*********************************************/
/*************************************************************************************/
new const TABLE[] = "AccMain"
new Handle:g_hTuple
new g_UserAuthId[33][35], g_State[33], g_UserId[33], g_DataLoad[33], g_DataSave[33]
enum {
SQL_OFFLINE,
SQL_REGISTER_USER,
SQL_LOGIN_USER,
SQL_LOAD_DATA,
SQL_SAVE_DATA,
SQL_EXTERNAL_DATA,
SQL_ONLINE
}
enum _:Forwards {
LOGIN,
USER_LOGIN_SUCCESS
}
new g_Forward[Forwards], g_ForwardResult, g_ED_Num
public plugin_natives() {
register_native("sm_guardado_id", "_sm_guardado_id")
register_native("sm_guardado_add_db", "_sm_guardado_add_db")
register_native("sm_guardado_get_handle", "_sm_guardado_get_handle")
register_native("sm_guardado_ed_ok", "_sm_guardado_ed_ok")
register_native("sm_guardado_ed_save", "_sm_guardado_ed_save")
}
public _sm_guardado_id(iPlugin, iParams) { // Native que devuelve el ID del registro
new id = get_param(1)
if(g_State[id] == SQL_OFFLINE)
return 0
return g_UserId[id]
}
public Handle:_sm_guardado_add_db(iPlugin, iParams) { // Native que registra que un plugin guarda/carga datos
g_ED_Num++
return g_hTuple
}
public Handle:_sm_guardado_get_handle(iPlugin, iParams) // Native que devuelve el Handle con la conexion
return g_hTuple
public _sm_guardado_ed_ok(iPlugin, iParams) { // Native que se llama cuando un plugin cargo correctamente los datos del usuario
new id = get_param(1)
if(g_State[id] != SQL_EXTERNAL_DATA || !is_user_connected(id))
return false
g_DataLoad[id]++
if(g_DataLoad[id] == g_ED_Num) // En caso de que el usuario cargo todos los datos que le corresponde lo loguea
func_LoginUser(id)
return true
}
public _sm_guardado_ed_save(iPlugin, iParams) { // Native que se llama cuando un plugin quiere guardar X dato
static uQuery[33][1024] /* Limite 1023 chars */, id, buffer[128]
id = get_param(1)
if(!g_DataSave[id])
copy(uQuery[id], charsmax(uQuery[]), "")
get_string(2, buffer, charsmax(buffer)) // Obtiene la Consulta
add(uQuery[id], charsmax(uQuery[]), buffer) // Almacena la consulta
g_DataSave[id]++
if(g_DataSave[id] == g_ED_Num) { // Si la cantidad de datos a guardar es la misma de los plugins registrados para guardar
SQL_ThreadQuery(g_hTuple, "DataHandler", uQuery[id], { 0, 0 }, 2) // Manda toda la consulta
g_DataSave[id] = 0
}
return true
}
public plugin_precache()
MySQLx_Init()
public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)
g_Forward[LOGIN] = CreateMultiForward("sm_guardado_login", ET_IGNORE, FP_CELL, FP_CELL, FP_CELL) // Forward que se llama cuando el usuario se esta logueando
g_Forward[USER_LOGIN_SUCCESS] = CreateMultiForward("sm_guardado_login_success", ET_IGNORE, FP_CELL) // Forward que se llama cuando el usuario se logueo
}
public sm_PlayerPreChooseMenu(id) { // MIRAR SERVERMANAGER INC
if(g_State[id] != SQL_ONLINE) {
if(g_State[id] != SQL_EXTERNAL_DATA) // Si el usuario NO esta cargando datos
func_CheckUser(id)
return PLUGIN_HANDLED
}
return PLUGIN_CONTINUE
}
public func_CheckUser(id) {
get_user_authid(id, g_UserAuthId[id], charsmax(g_UserAuthId[]))
static szQuery[128], iData[2]
iData[0] = id
iData[1] = SQL_LOGIN_USER
formatex(szQuery, charsmax(szQuery), "SELECT * FROM %s WHERE Usuario=^"%s^"", TABLE, g_UserAuthId[id])
SQL_ThreadQuery(g_hTuple, "DataHandler", szQuery, iData, 2)
return PLUGIN_HANDLED
}
public func_RegisterUser(id) {
static szQuery[128], iData[2]
iData[0] = id
iData[1] = SQL_REGISTER_USER
formatex( szQuery, charsmax( szQuery ), "INSERT INTO %s Usuario VALUES ^"%s^"", TABLE, g_UserAuthId[id])
SQL_ThreadQuery(g_hTuple, "DataHandler", szQuery, iData, 2)
return PLUGIN_HANDLED
}
public func_SaveData(id) {
new szQuery[ 128 ], iData[ 2 ]
iData[ 0 ] = id
iData[ 1 ] = SQL_SAVE_DATA
formatex(szQuery, charsmax(szQuery), "UPDATE %s SET FirstLogin='0' WHERE id='%d'", TABLE, g_UserId[id])
SQL_ThreadQuery(g_hTuple, "DataHandler", szQuery, iData, 2)
}
public func_LoadData(id) {
new szQuery[128], iData[2]
iData[0] = id
iData[1] = SQL_LOAD_DATA
formatex(szQuery, charsmax(szQuery), "SELECT id FROM %s WHERE Usuario=^"%s^"", TABLE, g_UserAuthId[id])
SQL_ThreadQuery(g_hTuple, "DataHandler", szQuery, iData, 2)
}
public DataHandler(failstate, Handle:Query, error[ ], error2, data[ ], datasize, Float:time) {
static id, iData
id = data[0]
switch(failstate) {
case TQUERY_CONNECT_FAILED: {
log_to_file("SQL_LOG.txt", "[%s] Error en la conexion al MySQL [%i]: %s", PLUGIN, error2, error)
return
}
case TQUERY_QUERY_FAILED:
log_to_file("SQL_LOG.txt", "[%s] Error en la consulta al MySQL [%i]: %s", PLUGIN, error2, error )
}
if(!is_user_connected(id))
return
iData = data[1]
switch(iData) {
case SQL_REGISTER_USER: {
g_State[id] = SQL_EXTERNAL_DATA
func_LoadData(id)
}
case SQL_LOGIN_USER: {
if(SQL_NumResults(Query)) {
g_UserId[id] = SQL_ReadResult(Query, 0)
g_State[id] = SQL_EXTERNAL_DATA
ExecuteForward(g_Forward[LOGIN], g_ForwardResult, g_UserId[id], id, false) // Avisamos que el usuario se esta logueando
// Manda el ID del registro, el ID del usuario y que no es la primera vez que se conecta
if(g_DataLoad[id] == g_ED_Num && g_State[id] != SQL_ONLINE) // Si el usuario cargo todos los datos y todavia no esta logueado
func_LoginUser(id)
}
else
func_RegisterUser(id)
}
case SQL_LOAD_DATA: {
g_UserId[id] = SQL_ReadResult(Query, 0)
ExecuteForward(g_Forward[LOGIN], g_ForwardResult, g_UserId[id], id, true) // Avisamos que el usuario se esta logueando y es su primera vez asi el resto de los plugins crean los datos necesarios
// Manda el ID del registro, el ID del usuario y que es la primera vez que se conecta
func_SaveData(id) // Guardamos que el usuario ya creo los datos principales en el resto de los plugins asi no se vuelven a crear
}
case SQL_SAVE_DATA:
if(g_DataLoad[id] == g_ED_Num && g_State[id] != SQL_ONLINE) // Si el usuario cargo todos los datos y todavia no esta logueado
func_LoginUser( id )
}
}
public func_LoginUser(id) {
g_State[id] = SQL_ONLINE
sm_CallChooseTeam(id) // MIRAR SERVERMANAGER INC
ExecuteForward(g_Forward[USER_LOGIN_SUCCESS], g_ForwardResult, id) // Avisamos que el usuario se logueo perfectamente y todos los datos fueron cargados
}
public client_putinserver(id) {
g_State[id] = SQL_OFFLINE
g_DataLoad[id] = 0
}
MySQLx_Init() {
g_hTuple = SQL_MakeDbTuple(SQL_HOST, SQL_USER, SQL_PASS, SQL_DATEBASE)
if(!g_hTuple) {
log_to_file("SQL_LOG.txt", "[%s] No se pudo conectar con la base de datos.", PLUGIN)
return pause("a")
}
new szQuery[256]
formatex(szQuery, charsmax(szQuery), "CREATE TABLE IF NOT EXISTS %s ( id int(10) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, Usuario varchar(35) NOT NULL UNIQUE KEY, FirstLogin int(2) NOT NULL DEFAULT '1' );", TABLE)
SQL_ThreadQuery(g_hTuple, "DataHandler", szQuery)
return PLUGIN_CONTINUE
}
public plugin_end()
SQL_FreeHandle(g_hTuple)
Veamos mas detalladamente.
Código PHP:
public _sm_guardado_id(iPlugin, iParams) { // Native que devuelve el ID del registro
new id = get_param(1)
if(g_State[id] == SQL_OFFLINE)
return 0
return g_UserId[id]
}
Acá no hay mucho que explicar.. el ID del registro es el ID que se crea cuando el usuario se registra por primera vez y con el cual vamos a identificar en el resto de los guardados
Código PHP:
public Handle:_sm_guardado_add_db(iPlugin, iParams) { // Native que registra que un plugin guarda/carga datos | Tambien devuelve el Handler de la conexion
g_ED_Num++
return g_hTuple
}
Acá le avisamos a nuestro plugin que otro plugin va a guardar/cargar datos
Código PHP:
public Handle:_sm_guardado_get_handle(iPlugin, iParams) // Native que devuelve el Handle con la conexion
return g_hTuple
La anotación lo dice todo
Código PHP:
public _sm_guardado_ed_ok(iPlugin, iParams) { // Native que se llama cuando un plugin cargo correctamente los datos del usuario
new id = get_param(1)
if(g_State[id] != SQL_EXTERNAL_DATA || !is_user_connected(id))
return false
g_DataLoad[id]++
if(g_DataLoad[id] == g_ED_Num) // En caso de que el usuario cargo todos los datos que le corresponde lo loguea
func_LoginUser(id)
return true
}
Acá lo que hacemos es avisarle a nuestro plugin principal que otro plugin ya cargo datos. Si nuestro plugin principal cargo la misma cantidad de datos de las que están registradas logueamos al usuario
Código PHP:
public _sm_guardado_ed_save(iPlugin, iParams) { // Native que se llama cuando un plugin quiere guardar X dato
static uQuery[33][1024] /* Limite 1023 chars */, id, buffer[128]
id = get_param(1)
if(!g_DataSave[id])
copy(uQuery[id], charsmax(uQuery[]), "")
get_string(2, buffer, charsmax(buffer)) // Obtiene la Consulta
add(uQuery[id], charsmax(uQuery[]), buffer) // Almacena la consulta
g_DataSave[id]++
if(g_DataSave[id] == g_ED_Num) { // Si la cantidad de datos a guardar es la misma de los plugins registrados para guardar
SQL_ThreadQuery(g_hTuple, "DataHandler", uQuery[id], { 0, 0 }, 2) // Manda toda la consulta
g_DataSave[id] = 0
}
return true
}
Muy importante esto.. Obtenemos las consultas con los datos para GUARDAR; si, el guardado de datos se hace TODO JUNTO así no se satura la DB; hay que tener en cuenta que tenemos un limite en este caso de 1023 caracteres para almacenar así que TENE EN CUENTA DE NO PASAR EL LIMITE y de ser necesario AUMENTALO
Código PHP:
g_Forward[LOGIN] = CreateMultiForward("sm_guardado_login", ET_IGNORE, FP_CELL, FP_CELL, FP_CELL) // Forward que se llama cuando el usuario se esta logueando
Esta forward le avisa a los otros plugins que un usuario tiene que cargar/crear los datos.. para diferenciarlo mandamos de ultimo parámetro 1/0 dependiendo si es la primera vez que se loguea o no. Ademas de esto manda el ID de registro y el ID del usuario.
Después de ver todo esto poray te estas pensando.. Para que voy a hacer esta locura si poniéndolo todo en un solo plugin voy a hacer lo mismo.. la diferencia esta en que es mas como ( A mi gusto ) tenerlo separado que andar buscando entre MUCHAS lineas de código y en lo personal es mucho mas fácil agregar/quitar datos a guardar/cargar.
Después de estas palabras... seguimos con el resto.
En el plugin esta todo explicado
sm_ExpMode
Código PHP:
#include <amxmodx>
#include <hamsandwich>
#include <sqlx>
new const PLUGIN[] = "ExpMode"
#define VERSION "1.0"
#define AUTHOR "[R]ak"
// Include? Armenlo ustedes..
/********************************************************/
/********/
native Handle:sm_guardado_add_db() /********/
native sm_guardado_id(id) /********/
native sm_guardado_ed_ok(id) /********/ //GuardadoCore Natives/Forwards
native sm_guardado_ed_save(id, query[]) /********/
/********/
forward sm_guardado_login(dbID, id, newuser) /********/
forward sm_guardado_login_success(id) /********/
/********/
/********************************************************/
new const TABLE[] = "expmode"
new const QUERY_HANDLE[] = "DataHandlerExpMode"
new Handle:g_hTuple
new g_UserLevel[33], g_UserExp[33], g_MaxPlayers
enum {
CREAR_DATOS = 1,
CARGAR_DATOS
}
#define Exp_Level(%1) ((127 * (%1 * 7)) * (50 + %1))
#define MarkBite(%0,%1) %0 |= (1 << (%1 & 31))
#define ClearBite(%0,%1) %0 &= ~(1 << (%1 & 31))
#define IsBite(%0,%1) (%0 & (1 << (%1 & 31)))
new g_BitInGame
#define is_valid_id(%0) (1 <= %0 <= g_MaxPlayers)
public plugin_init() {
register_plugin(PLUGIN, VERSION, AUTHOR)
register_clcmd("say /expmode", "cmd_ExpMode")
RegisterHam(Ham_TakeDamage, "player", "fw_PlayerTakeDamage_Post", true)
g_MaxPlayers = get_maxplayers()
MySQLx_Init()
}
public cmd_ExpMode(id) {
client_print(id, print_chat, "Nivel: %d | Exp: %d", g_UserLevel[id], g_UserExp[id])
return PLUGIN_HANDLED
}
public fw_PlayerTakeDamage_Post(victim, inflictor, attacker, Float:damage) {
if(!is_valid_id(victim) || !is_valid_id(attacker))
return
else if(!IsBite(g_BitInGame, victim) || !IsBite(g_BitInGame, attacker))
return
else if(attacker == victim)
return
Exp_Update(attacker, floatround(damage))
}
public sm_guardado_login(dbID, id, newuser) { // Cuando el player tiene que cargar/crear datos
new szQuery[128], iData[2]
iData[1] = id
if(newuser) { // Usuario nuevo. Creamos los datos
iData[0] = CREAR_DATOS
formatex(szQuery, charsmax(szQuery), "INSERT INTO %s id VALUES %d", TABLE, dbID)
if(!is_user_connected(id))
return
g_UserLevel[id] = 1
g_UserExp[id] = 0
}
else { // Usuario ya registrado. Cargamos los datos
iData[0] = CARGAR_DATOS
formatex(szQuery, charsmax(szQuery), "SELECT * FROM %s WHERE id=%d", TABLE, dbID)
}
SQL_ThreadQuery(g_hTuple, QUERY_HANDLE, szQuery, iData, 2)
}
public sm_guardado_login_success(id) // Cuando el player se logueo guardamos que ya esta jugando o listo para jugar
MarkBite(g_BitInGame, id)
public client_disconnect(id) {
new dbID = sm_guardado_id(id) // Obtenemos el ID de registro para guardar los datos
if(!dbID) // Sie l ID de registro es 0; osea que no existe
return
new szQuery[128]
formatex(szQuery, charsmax(szQuery), "UPDATE %s SET Nivel='%d', Exp='%d' WHERE id='%d'; ", TABLE, g_UserLevel[id], g_UserExp[id], dbID)
// Tene en cuenta que al final de la consulta dejo un espacio para otras consultas; se puede agregar eso directamente en el GuardadoCore entre cada consulta con un siempre add
sm_guardado_ed_save(id, szQuery) // Mandamos la consulta al GuardadoCore para que sea almacenada y mandada cuando corresponda
ClearBite(g_BitInGame, id)
}
public DataHandlerExpMode(failstate, Handle:Query, error[], error2, data[], datasize, Float:time) {
static id, iData;
iData = data[0]
id = data[1]
switch(failstate) {
case TQUERY_CONNECT_FAILED: {
log_to_file("SQL_LOG.txt", "[%s] Error en la conexion al MySQL [%i]: %s", PLUGIN, error2, error)
return
}
case TQUERY_QUERY_FAILED:
log_to_file("SQL_LOG.txt", "[%s] Error en la consulta al MySQL [%i]: %s", PLUGIN, error2, error )
}
if(!is_user_connected(id))
return
switch(iData) {
case CARGAR_DATOS: {
g_UserLevel[id] = SQL_ReadResult(Query, 1)
g_UserExp[id] = SQL_ReadResult(Query, 2)
sm_guardado_ed_ok(id) // Avisamos que se cargaron los datos
}
case CREAR_DATOS:
sm_guardado_ed_ok(id) // Avisamos que se crearon los datos
}
}
Exp_Update(id, exp) {
g_UserExp[id] += exp
if(g_UserExp[id] >= Exp_Level(g_UserLevel[id]))
g_UserLevel[id]++
}
MySQLx_Init() {
g_hTuple = sm_guardado_add_db() // Obtenemos le Handler de la conexion y avisamos que nuestro plugin va a cargar/guardar datos
if(!g_hTuple) {
log_to_file("SQL_LOG.txt", "[%s] No se pudo conectar con la base de datos.", PLUGIN)
return pause("a")
}
new szQuery[256]
formatex(szQuery, charsmax(szQuery), "CREATE TABLE IF NOT EXISTS %s ( id int(10) NOT NULL PRIMARY KEY , Nivel int(5) NOT NULL DEFAULT '1', Exp int(10) NOT NULL DEFAULT '0' );", TABLE)
SQL_ThreadQuery(g_hTuple, QUERY_HANDLE, szQuery, { 0, 0 }, 2)
return true
}
Si tienen alguna duda solo díganme y serán respondidas.
PD: No probé ningún plugin; puede q me dejara algún cabo suelto
Hi Milashkasiya
Soy un #Panda.. Soy solo un tipo que parece #común pero soy todo lo contrario; alguien #atípico en esta sociedad actual
Link
Soy un #Panda.. Soy solo un tipo que parece #común pero soy todo lo contrario; alguien #atípico en esta sociedad actual
Link