Tutorial Nvault (and nvault util)

Started by Astarasefk,

Previous topic - Next topic

Astarasefk

    TUTORIAL COMPLET NVAULT - Salvare date in AMXX
    De la zero pana la sisteme reale: Level/XP, VIP cu expirare, top, curatare vault


    CUPRINS
    [list=1]
    • Ce este nVault si cum a aparut
    • Cum arata o inregistrare (cheie, date, timestamp)
    • Ce folosim ca cheie: SteamID, IP sau nume?
    • Functiile nVault, explicate pe rand
    • Exemplul 1: sistem Level/XP complet
    • Exemplul 2: VIP cu expirare (folosind timestamp)
    • Exemplul 3: parcurgerea tuturor intrarilor (nvault_util)
    • Greseli frecvente
    • Sfaturi pentru performanta si siguranta


    1. CE ESTE NVAULT SI CUM A APARUT

    Cand folosesti nvault.inc, AMXX creeaza un fisier privat pentru pluginul tau, in folderul:

    addons/amxmodx/data/vault/
    Pe vremuri se folosea vault.inc (NU nvault.inc). Acela nu crea fisiere private, deci toate pluginele isi scriau cheile si datele in acelasi fisier, vault.ini.

    Ca sa nu se amestece datele, oamenii puneau prefixe sau sufixe la chei, gen LEVELMOD_%s. Gandeste-te la 5 plugini care salveaza toti cu cheia "Craxor": unul modelul, unul frag-urile, unul deaths:

    "Craxor" "1"
    "Craxor" "Vip.mdl"
    "Craxor" "0"

    Care e care? Imposibil de stiut, cheile se suprascriu intre ele.

    Cu nVault problema dispare: fiecare plugin isi deschide propriul vault, adica propriul fisier. Poti folosi direct SteamID-ul ca cheie, fara prefixe si fara griji ca alt plugin iti strica datele.


    2. CUM ARATA O INREGISTRARE

    Fiecare intrare din vault are 3 parti:

    "Cheie"  "Date"  "Timestamp"
    • Cheia = identificatorul cu care gasesti datele (ex: SteamID). Este unica: daca salvezi de 2 ori cu aceeasi cheie, a doua salvare inlocuieste prima.
    • Datele = un string cu ce vrei sa salvezi.
    • Timestamp = momentul salvarii, pus automat de nvault (secunde de la 1 ianuarie 1970). Il folosesti la curatare sau la expirari.
    Important: nvault salveaza doar string-uri. Daca ai mai multe valori (Level, XP, Bani), le pui intr-un singur string separate prin spatiu, iar la citire le desfaci cu parse().

    // Salvare: "150 3 2500"  (xp level bani)
    formatex(date, charsmax(date), "%i %i %i", xp, level, bani);

    // Citire:
    parse(date, xpArg, charsmax(xpArg), levelArg, charsmax(levelArg), baniArg, charsmax(baniArg));


    3. CE FOLOSIM CA CHEIE?

    • SteamID (recomandat): ramane acelasi indiferent de nume. Este cea mai sigura optiune.
    • Nume: nu il folosi! Oricine isi poate pune numele altcuiva si primeste contul lui.
    • IP: se schimba des (IP dinamic), deci jucatorii isi pierd progresul.
    Atentie la SteamID pe servere non-steam: unii jucatori primesc ID-uri gen STEAM_ID_LAN, VALVE_ID_LAN sau STEAM_ID_PENDING. Daca ii salvezi pe toti sub aceeasi cheie, vor imparti acelasi cont. Daca serverul tau are asemenea jucatori, verifica ID-ul inainte sa salvezi, sau foloseste nume/IP pentru ei.


    4. FUNCTIILE NVAULT, EXPLICATE PE ROND

    Include-ul de baza:
    #include <nvault>
    nvault_open(const name[])
    Deschide (sau creeaza, daca nu exista) vault-ul cu numele dat si returneaza un handle, adica un numar pe care il folosesti in toate celelalte functii. Se apeleaza o singura data, de obicei in plugin_init sau plugin_cfg. Daca nu reuseste, returneaza INVALID_HANDLE (-1).
    new g_Vault;
    g_Vault = nvault_open("LevelXP");   // creeaza data/vault/LevelXP.vault

    if(g_Vault == INVALID_HANDLE)
        set_fail_state("Nu pot deschide vault-ul!");

    nvault_close(vault)
    Inchide vault-ul si elibereaza fisierul. Se apeleaza in plugin_end. Daca uiti, poti avea probleme de blocare a fisierului sau date nescrise corect.
    public plugin_end()
        nvault_close(g_Vault);

    nvault_set(vault, const key[], const value[])
    Salveaza datele sub o cheie. Daca cheia exista deja, datele se suprascriu si timestamp-ul se actualizeaza la momentul curent.
    nvault_set(g_Vault, "STEAM_0:1:12345", "150 3");
    nvault_get(vault, const key[], data[], len)
    Citeste datele unei chei si le copiaza in string-ul data, taiat la maxim len caractere. Returneaza ceva diferit de 0 daca cheia exista, 0 daca nu exista (jucator nou). Ai grija ca bufferul sa fie destul de mare.
    new buffer[64];
    if(nvault_get(g_Vault, AuthID, buffer, charsmax(buffer)))
    {
        // cheia exista, buffer contine datele
    }
    else
    {
        // jucator nou
    }

    Varianta scurta (doar pentru un singur numar): nvault_get(vault, key) fara buffer returneaza direct valoarea ca numar intreg. Merge doar daca ai salvat un singur numar.
    nvault_set(g_Vault, AuthID, "500");
    new bani = nvault_get(g_Vault, AuthID);   // 500

    nvault_lookup(vault, const key[], value[], len, &timestamp)
    La fel ca nvault_get, dar iti da si timestamp-ul salvarii prin parametrul by-reference. Returneaza true daca cheia exista. O vei folosi cand ai nevoie sa stii cand a fost salvat ceva (expirari, bonus zilnic, ultima conectare).
    new date[64], timestamp;
    if(nvault_lookup(g_Vault, AuthID, date, charsmax(date), timestamp))
    {
        server_print("Salvat acum %d secunde", get_systime() - timestamp);
    }

    nvault_pset(vault, const key[], const value[])
    La fel ca nvault_set, dar intrarea devine permanenta: nu este stearsa de nvault_prune, indiferent cat de veche e. Foloseste-o pentru date care nu trebuie sa dispara niciodata (admini, ban-uri permanente).
    nvault_pset(g_Vault, AuthID, "admin");
    nvault_touch(vault, const key[], timestamp = -1)
    Schimba doar timestamp-ul unei chei, fara sa-i modifice datele. Fara al treilea parametru, pune ora curenta. Util ca sa "reinnoiesti" o intrare activa, ca sa nu fie stearsa de prune.
    nvault_touch(g_Vault, AuthID);            // timestamp = acum
    nvault_touch(g_Vault, AuthID, 1700000000); // timestamp ales de tine

    nvault_remove(vault, const key[])
    Sterge o singura cheie din vault (resetarea unui jucator, expirarea unui VIP).
    nvault_remove(g_Vault, AuthID);
    nvault_prune(vault, start, end)
    Sterge toate intrarile al caror timestamp se afla intre start si end (cele facute cu nvault_pset sunt ignorate). Asa cureti jucatorii inactivi si vault-ul nu creste la infinit.
    // Sterge tot ce e mai vechi de 60 de zile
    nvault_prune(g_Vault, 0, get_systime() - (60 * 86400));
    Apeleaz-o rar, de exemplu la plugin_init, nu la fiecare runda.

    nvault_clear(vault)
    PERICULOS! Sterge toate intrarile din vault, inclusiv pe cele permanente. Foloseste-o doar pentru un reset complet (de exemplu comanda de admin "reset sezon"), cu confirmare.
    nvault_clear(g_Vault);

    5. EXEMPLUL 1: SISTEM LEVEL/XP COMPLET

    Ce face acest cod:
    • Incarca datele la client_authorized (atunci SteamID-ul e sigur disponibil, la client_putinserver uneori nu e)
    • Salveaza la deconectare, la schimbarea hartii si periodic (ca sa nu pierzi progresul daca pica serverul)
    • Foloseste un flag g_Loaded ca sa nu suprascrie datele bune cu valorile default, daca incarcarea n-a avut loc
    • Ignora boti si HLTV

    #include <amxmodx>
    #include <nvault>

    #define PLUGIN  "Level XP nVault"
    #define VERSION "2.0"
    #define AUTHOR  "Craxor"

    new g_Vault;
    new g_PlayerXP[33], g_PlayerLevel[33];
    new bool:g_Loaded[33];

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

        g_Vault = nvault_open("LevelXP");

        if(g_Vault == INVALID_HANDLE)
            set_fail_state("Nu pot deschide vault-ul LevelXP!");

        // Curatam jucatorii inactivi de peste 90 de zile
        nvault_prune(g_Vault, 0, get_systime() - (90 * 86400));

        // Salvare automata la fiecare 5 minute
        set_task(300.0, "SaveAll", _, _, _, "b");
    }

    public plugin_end()
    {
        SaveAll();
        nvault_close(g_Vault);
    }

    public client_authorized(id)
    {
        g_PlayerXP[id]    = 0;
        g_PlayerLevel[id] = 1;
        g_Loaded[id]      = false;

        if(is_user_bot(id) || is_user_hltv(id))
            return;

        LoadData(id);
    }

    public client_disconnected(id)
    {
        if(g_Loaded[id])
            SaveData(id);

        g_Loaded[id] = false;
    }

    public SaveAll()
    {
        for(new id = 1; id <= 32; id++)
        {
            if(is_user_connected(id) && g_Loaded[id])
                SaveData(id);
        }
    }

    public SaveData(id)
    {
        new AuthID[35];
        get_user_authid(id, AuthID, charsmax(AuthID));

        // Format: "xp level"
        new VaultData[64];
        formatex(VaultData, charsmax(VaultData), "%i %i", g_PlayerXP[id], g_PlayerLevel[id]);

        nvault_set(g_Vault, AuthID, VaultData);
    }

    public LoadData(id)
    {
        new AuthID[35];
        get_user_authid(id, AuthID, charsmax(AuthID));

        new DataBuffer[64];

        if(nvault_get(g_Vault, AuthID, DataBuffer, charsmax(DataBuffer)))
        {
            // Desfacem "xp level" in 2 argumente
            new XpArg[11], LevelArg[11];
            parse(DataBuffer, XpArg, charsmax(XpArg), LevelArg, charsmax(LevelArg));

            g_PlayerXP[id]    = str_to_num(XpArg);
            g_PlayerLevel[id] = str_to_num(LevelArg);
        }
        // Daca nu exista, ramane cu valorile default (jucator nou)

        g_Loaded[id] = true;
    }

    Cum citesti rezultatul: deschide fisierul LevelXP.vault cu nVault Editor. O linie va arata cam asa:
    "STEAM_0:1:12345"   "150 3"   "1735689600"adica cheia (SteamID), datele (xp level) si timestamp-ul.


    6. EXEMPLUL 2: VIP CU EXPIRARE (FOLOSIND TIMESTAMP)

    Aici folosim nvault_lookup ca sa aflam cand a fost dat VIP-ul. In vault salvam durata in zile, iar timestamp-ul pus automat de nvault ne spune de cand incepe.

    #include <amxmodx>
    #include <nvault>

    new g_VipVault;

    public plugin_init()
    {
        register_plugin("VIP Expirare", "1.0", "Craxor");
        g_VipVault = nvault_open("VipExpirare");

        if(g_VipVault == INVALID_HANDLE)
            set_fail_state("Nu pot deschide vault-ul VIP!");
    }

    public plugin_end()
        nvault_close(g_VipVault);

    // Da VIP unui jucator pentru X zile
    stock AddVip(id, zile)
    {
        new AuthID[35], data[16];
        get_user_authid(id, AuthID, charsmax(AuthID));

        num_to_str(zile, data, charsmax(data));
        nvault_set(g_VipVault, AuthID, data);   // timestamp = acum
    }

    // Verifica daca jucatorul are VIP activ
    stock bool:HasVip(id)
    {
        new AuthID[35], data[16], timestamp;
        get_user_authid(id, AuthID, charsmax(AuthID));

        if(!nvault_lookup(g_VipVault, AuthID, data, charsmax(data), timestamp))
            return false;

        new expira = timestamp + (str_to_num(data) * 86400);

        if(get_systime() >= expira)
        {
            nvault_remove(g_VipVault, AuthID);   // a expirat, il stergem
            return false;
        }

        return true;
    }

    Idee: pentru VIP permanent foloseste nvault_pset in loc de nvault_set si sari peste verificarea de expirare.


    7. EXEMPLUL 3: PARCURGEREA TUTUROR INTRARILOR (nvault_util)

    Cu nvault simplu poti citi doar o cheie pe care o cunosti. Daca vrei sa treci prin toate intrarile (pentru un top, de exemplu), folosesti include-ul separat nvault_util:

    #include <nvault_util>
    Functiile principale:
    • nvault_util_open(const name[]) deschide vault-ul pentru parcurgere si returneaza un handle
    • nvault_util_count(handle) returneaza numarul total de intrari
    • nvault_util_read(handle, position, key[], keyLen, value[], valueLen, &timestamp) citeste intrarea de la pozitia data si returneaza pozitia urmatoare
    • nvault_util_close(handle) inchide handle-ul
    public ListAll()
    {
        new util = nvault_util_open("LevelXP");

        if(util == INVALID_HANDLE)
            return;

        new count = nvault_util_count(util);
        new pos = 0;
        new key[35], value[64], timestamp;

        for(new i = 0; i < count; i++)
        {
            pos = nvault_util_read(util, pos, key, charsmax(key), value, charsmax(value), timestamp);
            server_print("Cheie: %s | Date: %s", key, value);
        }

        nvault_util_close(util);
    }

    Din aceste date poti construi un top: parcurgi, desfaci fiecare valoare cu parse si sortezi dupa XP. Apeleaza parcurgerea rar (o comanda de admin, finalul hartii), nu la fiecare runda, pentru ca pe un vault mare dureaza.


    8. GRESELI FRECVENTE

    • Scrii AuthId intr-un loc si AuthID in altul. Pawn face diferenta intre litere mari si mici, deci sunt doua variabile diferite!
    • Uiti nvault_close in plugin_end.
    • Folosesti numele ca cheie in loc de SteamID.
    • Nu verifici INVALID_HANDLE dupa nvault_open, iar pluginul ruleaza fara vault si nu salveaza nimic.
    • Nu resetezi variabilele cand intra un jucator, iar noul jucator mosteneste datele celui care a plecat de pe acelasi slot.
    • Salvezi datele default peste cele reale daca load-ul nu s-a facut (de aceea folosim g_Loaded).
    • Buffer prea mic pentru date, iar string-ul se taie si parse citeste prost.
    • Citesti in alta ordine decat ai salvat. Daca salvezi "xp level", parse trebuie sa citeasca tot xp, apoi level.
    • Salvezi si boti sau HLTV si umpli vault-ul degeaba.
    • Salvezi la fiecare kill. E scriere pe disc de zeci de ori pe secunda, deci salvezi la disconnect, la schimbarea hartii si periodic.

    9. SFATURI PENTRU PERFORMANTA SI SIGURANTA

    • Deschide vault-ul o singura data, in plugin_init, nu de fiecare data cand salvezi.
    • Foloseste formatex in loc de format: e mai rapid si mai sigur.
    • Foloseste charsmax() la toate bufferele, ca sa eviti depasirile.
    • Fa backup periodic la fisierele .vault din data/vault. Un crash la momentul nepotrivit sau o stergere accidentala si pierzi toate conturile.
    • Foloseste nVault Editor ca sa vezi si sa editezi vault-urile cu usurinta.
    • Un vault per plugin (sau per sistem) e mai curat decat unul mare cu de toate.
    • Daca salvezi texte cu spatii (de exemplu nume), pune-le intre ghilimele in string, ca parse sa nu le rupa, sau pune-le ultimele.
    • Planifica un nvault_prune ocazional ca vault-ul sa nu creasca la infinit, iar pentru intrarile care nu trebuie sterse niciodata foloseste nvault_pset.

    Gata! Ai acum tot ce trebuie ca sa salvezi orice in nVault.
    Intrebari sau probleme? Scrieti mai jos, va ajutam! :)
    terra fanclub.

    Astarasefk

    terra fanclub.