[TUT] Modularizar un sistema de guardado por SQL
#1
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

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_ForwardResultg_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(iPluginiParams) { // 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(iPluginiParams) { // Native que registra que un plugin guarda/carga datos
    
    
g_ED_Num++
    
    return 
g_hTuple
    
}

public 
Handle:_sm_guardado_get_handle(iPluginiParams// Native que devuelve el Handle con la conexion
    
return g_hTuple

public _sm_guardado_ed_ok(iPluginiParams) { // 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(iPluginiParams) { // Native que se llama cuando un plugin quiere guardar X dato
    
    
static uQuery[33][1024/* Limite 1023 chars */idbuffer[128]
    
    
id get_param(1)
    
    if(!
g_DataSave[id])
        
copy(uQuery[id], charsmax(uQuery[]), "")
        
    
get_string(2buffercharsmax(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}, 2// Manda toda la consulta
        
        
g_DataSave[id] = 0
        
    
}
    
    return 
true
    
}

public 
plugin_precache()
    
MySQLx_Init()

public 
plugin_init() {
    
    
register_plugin(PLUGINVERSIONAUTHOR)

    
g_Forward[LOGIN] = CreateMultiForward("sm_guardado_login"ET_IGNOREFP_CELLFP_CELLFP_CELL// Forward que se llama cuando el usuario se esta logueando
    
    
g_Forward[USER_LOGIN_SUCCESS] = CreateMultiForward("sm_guardado_login_success"ET_IGNOREFP_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(idg_UserAuthId[id], charsmax(g_UserAuthId[]))
    
    static 
szQuery[128], iData[2]
    
    
iData[0] = id
    
    iData
[1] = SQL_LOGIN_USER
    
    formatex
(szQuerycharsmax(szQuery), "SELECT * FROM %s WHERE Usuario=^"%s^""TABLEg_UserAuthId[id])
    
    
SQL_ThreadQuery(g_hTuple"DataHandler"szQueryiData2)
    
    return 
PLUGIN_HANDLED
    
}

public 
func_RegisterUser(id) {
    
    static 
szQuery[128], iData[2]
    
    
iData[0] = id
    
    iData
[1] = SQL_REGISTER_USER
        
    formatex
szQuerycharsmaxszQuery ), "INSERT INTO %s Usuario VALUES ^"%s^""TABLEg_UserAuthId[id])
    
    
SQL_ThreadQuery(g_hTuple"DataHandler"szQueryiData2)
    
    return 
PLUGIN_HANDLED
    
}
public 
func_SaveData(id) {
        
    new 
szQuery128 ], iData]
    
    
iData] = id
    
    iData
] = SQL_SAVE_DATA
    
    formatex
(szQuerycharsmax(szQuery), "UPDATE %s SET FirstLogin='0' WHERE id='%d'"TABLEg_UserId[id])
    
    
SQL_ThreadQuery(g_hTuple"DataHandler"szQueryiData2)
    
}

public 
func_LoadData(id) {
    
    new 
szQuery[128], iData[2]
    
    
iData[0] = id
    
    iData
[1] = SQL_LOAD_DATA
    
    formatex
(szQuerycharsmax(szQuery), "SELECT id FROM %s WHERE Usuario=^"%s^""TABLEg_UserAuthId[id])
    
    
SQL_ThreadQuery(g_hTuple"DataHandler"szQueryiData2)
    
}

public 
DataHandler(failstateHandle:Queryerror[ ], error2data[ ], datasizeFloat:time) {
    
    static 
idiData
    
    id 
data[0]
    
    switch(
failstate) {
        
        case 
TQUERY_CONNECT_FAILED: {
            
            
log_to_file("SQL_LOG.txt""[%s] Error en la conexion al MySQL [%i]: %s"PLUGINerror2error)
            
            return
            
        }
        
        case 
TQUERY_QUERY_FAILED:
            
log_to_file("SQL_LOG.txt""[%s] Error en la consulta al MySQL [%i]: %s"PLUGINerror2error )
            
    }
    
    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(Query0)
                    
                
g_State[id] = SQL_EXTERNAL_DATA
                    
                ExecuteForward
(g_Forward[LOGIN], g_ForwardResultg_UserId[id], idfalse// 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(Query0)
                
            
ExecuteForward(g_Forward[LOGIN], g_ForwardResultg_UserId[id], idtrue// 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_LoginUserid )
                
    }
}

public 
func_LoginUser(id) {
    
    
g_State[id] = SQL_ONLINE
    
    sm_CallChooseTeam
(id// MIRAR SERVERMANAGER INC
    
    
ExecuteForward(g_Forward[USER_LOGIN_SUCCESS], g_ForwardResultid// 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_HOSTSQL_USERSQL_PASSSQL_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(szQuerycharsmax(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(iPluginiParams) { // 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(iPluginiParams) { // 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(iPluginiParams// 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(iPluginiParams) { // 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(iPluginiParams) { // Native que se llama cuando un plugin quiere guardar X dato
    
    
static uQuery[33][1024/* Limite 1023 chars */idbuffer[128]
    
    
id get_param(1)
    
    if(!
g_DataSave[id])
        
copy(uQuery[id], charsmax(uQuery[]), "")
        
    
get_string(2buffercharsmax(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}, 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_IGNOREFP_CELLFP_CELLFP_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(idquery[])        /********/
                        /********/
forward sm_guardado_login(dbIDidnewuser)    /********/
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(PLUGINVERSIONAUTHOR)
    
    
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(idprint_chat"Nivel: %d | Exp: %d"g_UserLevel[id], g_UserExp[id])
    
    return 
PLUGIN_HANDLED
    
}

public 
fw_PlayerTakeDamage_Post(victiminflictorattackerFloat:damage) {
    
    if(!
is_valid_id(victim) || !is_valid_id(attacker))
        return
        
    else if(!
IsBite(g_BitInGamevictim) || !IsBite(g_BitInGameattacker))
        return
        
    else if(
attacker == victim)
        return
        
    
Exp_Update(attackerfloatround(damage))
    
}

public 
sm_guardado_login(dbIDidnewuser) { // 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
(szQuerycharsmax(szQuery), "INSERT INTO %s id VALUES %d"TABLEdbID)
        
        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
(szQuerycharsmax(szQuery), "SELECT * FROM %s WHERE id=%d"TABLEdbID)
        
    }
    
    
SQL_ThreadQuery(g_hTupleQUERY_HANDLEszQueryiData2)
    
}

public 
sm_guardado_login_success(id// Cuando el player se logueo guardamos que ya esta jugando o listo para jugar
    
MarkBite(g_BitInGameid)

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(szQuerycharsmax(szQuery), "UPDATE %s SET Nivel='%d', Exp='%d' WHERE id='%d'; "TABLEg_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(idszQuery// Mandamos la consulta al GuardadoCore para que sea almacenada y mandada cuando corresponda
    
    
ClearBite(g_BitInGameid)
    
}

public 
DataHandlerExpMode(failstateHandle:Queryerror[], error2data[], datasizeFloat:time) {
        
    static 
idiData;
        
    
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"PLUGINerror2error)
            
            return
            
        }
        
        case 
TQUERY_QUERY_FAILED:
            
log_to_file("SQL_LOG.txt""[%s] Error en la consulta al MySQL [%i]: %s"PLUGINerror2error )
            
    }
    
    if(!
is_user_connected(id))
        return
    
    switch(
iData) {
    
        case 
CARGAR_DATOS: {
            
            
g_UserLevel[id] = SQL_ReadResult(Query1)
        
            
g_UserExp[id] = SQL_ReadResult(Query2)
            
            
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(idexp) {
    
    
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(szQuerycharsmax(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_hTupleQUERY_HANDLEszQuery, { 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

Whatever
Hi Milashkasiya CrabCrab

Soy un #Panda.. Soy solo un tipo que parece #común pero soy todo lo contrario; alguien #atípico en esta sociedad actual

[Imagen: xl0jvd-5.png]
Link
Responder
#2
Esta muy bueno!!
Voy a copiarte la idea pero voy a hacerlo con el modulo mejorado de Destro Lengua

Podrías hacerlo mismo Sonrisa
Responder


Salto de foro:


Usuarios navegando en este tema: 1 invitado(s)