Sveiki, tai viena iš mano pamokų rašytų kitame forume. Čia ji yra atnaujinta, performatuota ir patikrinta. Bet jos tema lieka ta pati.
Elementarių žaidėjų duomenų saugojimas. Pamokoje aptarsiu ir parodysiu kodą skirtą saugoti/krauti populiarią žaidėją informaciją kaip pinigai.
Kodas bus rašomas trejomis BlueG MySQL plugin versijomis:
Pirmiausia pradėsiu nuo kodo kuris galios visoms versijoms. Tai yra duomenų struktūros mūsų duomenims.
//Susikuriame enum kad būtų lengviau sekti informaciją, nors galime naudoti ir DEFINE ar const.
enum E_PLAYER_DATA {
SqlId,
Vardas[MAX_PLAYER_NAME],
Pinigai,
Xp,
Darbas,
Float:Hp
};
new pInfo[ MAX_PLAYERS ][ E_PLAYER_DATA ]; // Sukuriame masyvą.
Taip pat jums reikės susikurti lentelę duomenų bazėje. Jos struktūra ir paaiškinimai kaip sukurti nemokantiems, pamokos pabaigoje.
Pradėsim nuo seniausios mano aptariamos versijos.
R6
public OnGameModeInit()
{
// Žinoma tai nebūtina, bet padeda ieškoti klaidų.
mysql_debug(true);
mysql_connect("localhost","vartotojas", "duomenu_baze","slaptazodis");
return 1;
}
stock LoadStats(playerid) // Susikursime funkciją kad krauti galėtumėme krauti duomenis kada tik panorėjus
{
new name[MAX_PLAYER_NAME],query[70];
GetPlayerName(playerid, name, sizeof(name));
format(query, sizeof(query), "SELECT * FROM zaidejai WHERE Vardas = '%s'",name); // Pasirenkame viską iš lentelės "zaidejai" kur vardas yra lygus kintamajam "name"
mysql_query(query); // Išsiunčiame užklausą
mysql_store_result(); // Išsaugome rezultatą
if(mysql_retrieve_row()) // Šita funkcija perkelia vidinį "skirtuką" į sekančią eilutę, jeigu nėra daugiau eilučių, gražina 0.
{
new tmp[32];
// Mysql_fetch_field_row funkcija kaip parametrą priema tik "string" tipo kintamuosius, todėl sukūrėme kintamąjį "tmp"
// Pakeisti iš "string" tipo į "integer" naudosime "strval" funkciją "strval"
// Pakeisti iš "string" tipo į "float" tipą naudosime funkciją "floatstr"
mysql_fetch_field_row(tmp,"ID");
pInfo[ playerid ][ SqlId ] = strval(tmp);
mysql_fetch_field_row(pInfo[ playerid ][ Vardas ], "Vardas");
mysql_fetch_field_row(tmp, "Pinigai");
pInfo[ playerid ][ Pinigai ]= strval(tmp);
mysql_fetch_field_row(tmp, "Xp");
pInfo[ playerid ][ Xp ] = strval(tmp);
mysql_fetch_field_row(tmp, "Darbas");
pInfo[ playerid ][ Darbas ] = strval(tmp);
mysql_fetch_field_row(tmp, "Hp");
pInfo[ playerid ][ Hp ] = floatstr(tmp);
}
mysql_free_result(); // "Išlaisviname" gautą rezultą iš atminties
return 1;
}
stock SaveStats(playerid) // Susikursime funkciją kad krauti galėtumėme kada tik panorėjus
{
new query[100];
//Suformatuojame užklausą kuri "atnaujins" lentelę "zaidejai" kur stulpelis "ID" lygus kintamajama "pInfo[ playerid ][ SqlId ]"
format(query,sizeof(query),"UPDATE zaidejai SET Pinigai = %d, Xp = %d, Darbas = %d, Hp = %f WHERE ID = %d",
pInfo[ playerid ][ Pinigai ], pInfo[ playerid ][ Xp ], pInfo[ playerid ][ Darbas ], pInfo[ playerid ][ Hp ], pInfo[ playerid ][ SqlId ]);
mysql_query(query);
return 1;
}
Pastaba. R6 yra klaida dėl kurios kartais mysql_fetch_field_row funkcija priverčia crash'inti serverį. Jei jums taip atsitiko, yra alternatyva naudojant sscanf plugin
Naudojant šią alternatyva mūsų funkcija LoadStats
atrodytų taip:
stock LoadStats(playerid)
{
new name[MAX_PLAYER_NAME], query[100];
GetPlayerName(playerid, name, sizeof(name));
format(query, sizeof(query), "SELECT * FROM zaidejai WHERE Vardas = '%s'",name); // Pasirenkame viską iš lentelės "zaidejai" kur vardas yra lygus kintamajam "name"
mysql_query(query); // Išsiunčiame užklausą
mysql_store_result(); // Išsaugome rezultatą
if(mysql_fetch_row_format(query, "|")) // Ši funkcija paims VISŲ stulpelių vertes ir gražins jį kaip vieną string, kur stulpeliai bus atskirti nurodytu simboliu. Šiuo atveju '|'
{
sscanf(query, "p<|>e<is[24]iiif>", pInfo[ playerid ]);
}
mysql_free_result(); // "Išlaisviname" gautą rezultą iš atminties
return 1;
}
Aptarkime sscanf eilutę.
- p<|> tai nurodo kokiu simboliu išskaidysime eilutę. Tai turėtų būti tas pats simbolis kurį norodėte mysql_fetch_row_format funkcijai
- e< tai nurodo kad sekanti informacija bus kraunama į masyvą su enum.
- is[24]iiif tai nurodo kokių duomenų tipų informaciją krausime, tai yra: integer(skaičius) string(tekstas) integer(skaičius) integer(skaičius) integer(skaičius) float(skaičius su kableliu)
-
tai nurodo kad baigiasi informacija kuri bus kraunama į masyvą su enum.
Šis būdas atrodo dar geresnis, ar ne? Ne. Jis pasikliauja tuo kad tiek enum'o tvarka, tiek stulpelių tvarka bus vienoda. Žinoma, galima krauti ne į masyvą, o kaip atskirus kintamuosius, tokiu atveju nereikėtų stebėti enum tvarkos, bet vistiek duomenų bazės stulpelių tvarka turėtų didelę įtaka.
Su sscanf taip pat galima krauti paprastus masyvus, praleisti kai kuriuos duomenis. Daugiau informacijos rasite sscanf Wiki puslapyje.
R7
Su šia versija atsirado "threaded" užklausos. Jos stipriai pagreitina veikimą, tačiau atrodo sunkesnės naudotojams.
Pirmiausia mums reikės dar vieno papildomo kintamojo
// Tai paprastas globalus kintamasis kuriame laikysim skaičių kurį gražina mysql_connect. Tas skaičius leis atskirti prisijungimus į MySQL. Šiame pavyzdyje naudojamas vienas, bet galima naudoti ir daugiau.
new DbHandle;
public OnGameModeInit()
{
// Žinoma tai nebūtina, bet padeda ieškoti klaidų.
mysql_debug(true);
DbHandle = mysql_connect("localhost","vartotojas","duomenu_baze","slaptazodis");
return 1;
}
stock LoadStats(playerid)
{
new query[70], name[ MAX_PLAYER_NAME ];
GetPlayerName(playerid, name, sizeof(name));
format(query, sizeof(query), "SELECT * FROM zaidejai WHERE Vardas = '%s'", name);
// DbHandle yra mūsų kintamasis su prisijungimo numeriu. Jį gavome iš mysql_connect
// Trečiasis argumentas yra "true". Jis nurodo kad norime kad gauta informaciją iš MySQL būtų išsaugota.
// "OnPlayerInfoLoad" yra funkcija kurią MySQL iškvies gavus duomenis.
// "i" nurodo kad norime pridėti vieną integer(sveikas skaičius) tipo kintamąjį
// playerid yra tas mūsų kintamasis.
mysql_function_query(DbHandle, query, true, "OnPlayerInfoLoad", "i", playerid);
}
// funkcija kurią nurodėme mysql_function_query funkcijoje. Ši funkcija PRIVALO būti public.
forward OnPlayerInfoLoad(playerid);
public OnPlayerInfoLoad(playerid)
{
new rows, fields;
cache_get_data(rows, fields);
if(rows)
{
new tmp[32];
// cache_get_field_content gaus duomenis iš nurodyto stulpelio ir gražins juos string formatu.
// 0 nurodo eilutės indeksą iš kurios norime paimti. Taip, pirmoji eilutė yra 0.
// tmp yra kintamasis kuriame bus mūsų informacija.
cache_get_field_content(0, "ID", tmp);
pInfo[ playerid ][ SqlId ] = strval(tmp);
cache_get_field_content(0, "Vardas", pInfo[ playerid ][ Vardas ]);
cache_get_field_content(0, "Pinigai", tmp);
pInfo[ playerid ][ Pinigai ] = strval(tmp);
cache_get_field_content(0, "Xp", tmp);
pInfo[ playerid ][ Xp ] = strval(tmp);
cache_get_field_content(0, "Darbas", tmp);
pInfo[ playerid ][ Darbas ] = strval(tmp);
cache_get_field_content(0, "Hp", tmp);
pInfo[ playerid ][ Hp ] = floatstr(tmp);
}
return 1;
}
stock SaveStats(playerid) // Susikursime funkciją kad krauti galėtumėme kada tik panorėjus
{
new query[100];
//Suformatuojame užklausą kuri "atnaujins" lentelę "zaidejai" kur stulpelis "ID" lygus kintamajama "pInfo[ playerid ][ SqlId ]"
format(query,sizeof(query),"UPDATE zaidejai SET Pinigai = %d, Xp = %d, Darbas = %d, Hp = %f WHERE ID = %d",
pInfo[ playerid ][ Pinigai ], pInfo[ playerid ][ Xp ], pInfo[ playerid ][ Darbas ], pInfo[ playerid ][ Hp ], pInfo[ playerid ][ SqlId ]);
// DbHandle yra mūsų kintamasis su prisijungimo numeriu. Jį gavome iš mysql_connect
// Trečias argumentas yra "false", jis nurodo kad jokios gautos informacijos iš mysql nereikia išsaugoti.
// Taip pat nereikia jokios funkcijos iškviesti.
mysql_function_query(DbHandle, query, false, "", "");
return 1;
}
Tikriausiai keisčiausias dalykas tai yra ta mysql_function_query, tiksliau kodėl jai reikia nurodyti kitą funkciją? Viskas dėl tos naujovės atsiradusios R7, "threading". Deja Lietuviško atitikmens tam žodžiui nežinau... Tai tiesiogiai verčiant "thread" yra "siūlas". SAMP serveris yra vykdomas tik vienam siūle. Tai reiškia kad visiškai viskas yra vykdoma iš eilės, nors dažnai atrodo kad tikrai keli dalykai vyksta vienu metu. Taip nėra. Tačiau, mysql plugin susikuria savo nuosava siūlą kuriame vykdo užklausas į duomenų bazę. Todėl serveriui nereikia laukti kol tai bus padaryta. Kadangi serveris nelaukia mysql atsakymo, reikia nurodyti kokią funkciją plugin iškvies grįžęs iš duomenų bazės.
R39
Veikimo principas nuo R7 smarkiai nesikeitė. Tačiau buvo pridėta begalė funkcijų kurios palengvina darbą. Galima netgi naudoti MySQL neparašant nei vienos užklausos! Tačiau, šioje pamokoje aptarsiu tik būdą su užklausomis. Būdas su užklausomis(tradicinis)
// Tai paprastas globalus kintamasis kuriame laikysim skaičių kurį gražina mysql_connect. Tas skaičius leis atskirti prisijungimus į MySQL. Šiame pavyzdyje naudojamas vienas, bet galima naudoti ir daugiau.
new DbHandle;
public OnGameModeInit()
{
mysql_log();
DbHandle = mysql_connect("localhost","root","test","");
return 1;
}
stock LoadStats(playerid)
{
new query[70], name[ MAX_PLAYER_NAME ];
GetPlayerName(playerid, name, sizeof(name));
// Ši funkcija veikia taip pat kaip ir format, tik turi papildomą parametrą pradžioje ir duoda %e apie kurį skaitykite po kodu.
mysql_format(DbHandle, query, sizeof(query), "SELECT * FROM zaidejai WHERE Vardas = '%e'", name);
// DbHandle yra mūsų kintamasis su prisijungimo numeriu. Jį gavome iš mysql_connect
// "OnPlayerInfoLoad" yra funkcija kurią MySQL iškvies gavus duomenis.
// "i" nurodo kad norime pridėti vieną integer(sveikas skaičius) tipo kintamąjį
// playerid yra tas mūsų kintamasis.
mysql_pquery(DbHandle, query, "OnPlayerInfoLoad", "i", playerid);
}
// funkcija kurią nurodėme mysql_pquery funkcijoje. Ši funkcija PRIVALO būti public.
forward OnPlayerInfoLoad(playerid);
public OnPlayerInfoLoad(playerid)
{
if(cache_get_row_count())
{
// Trims duomenų tipams yra trys funkcijos:
// String - cache_get_field_content
// Integer - cache_get_field_content_int
// Float - cache_get_field_content_float
// Visų pirmasis parametras yra 0, tai yra eilutės indeksas. Pirmosios eilutės indeksas visada bus 0.
pInfo[ playerid ][ SqlId ] = cache_get_field_content_int(0, "ID");
cache_get_field_content(0, "Vardas", pInfo[ playerid ][ Vardas ], DbHandle, MAX_PLAYER_NAME); // Kodėl yra daugiau argumentų, žiūrėti po kodu.
pInfo[ playerid ][ Pinigai ] = cache_get_field_content_int(0, "Pinigai");
pInfo[ playerid ][ Xp ] = cache_get_field_content_int(0, "Xp");
pInfo[ playerid ][ Darbas ] = cache_get_field_content_int(0, "Darbas");
pInfo[ playerid ][ Hp ] = cache_get_field_content_float(0, "Hp");
}
return 1;
}
stock SaveStats(playerid) // Susikursime funkciją kad krauti galėtumėme kada tik panorėjus
{
new query[100];
//Suformatuojame užklausą kuri "atnaujins" lentelę "zaidejai" kur stulpelis "ID" lygus kintamajama "pInfo[ playerid ][ SqlId ]"
mysql_format(DbHandle, query,sizeof(query),"UPDATE zaidejai SET Pinigai = %d, Xp = %d, Darbas = %d, Hp = %f WHERE ID = %d",
pInfo[ playerid ][ Pinigai ], pInfo[ playerid ][ Xp ], pInfo[ playerid ][ Darbas ], pInfo[ playerid ][ Hp ], pInfo[ playerid ][ SqlId ]);
// DbHandle yra mūsų kintamasis su prisijungimo numeriu. Jį gavome iš mysql_connect
// Taip pat nereikia jokios funkcijos iškviesti.
mysql_pquery(DbHandle, query);
return 1;
}
"%e". Minėjau kad mysql_format tai prideda, o kas tai? Įrašinėjant vartotojo įvesta tekstą mes nežinome kas jame bus. Užklausose string pabaigą žymi viena kabutė ' . O kas bus jei žaidėjas įvest tokį simbolį? Paprastai nieko gero. Todėl tokius simbolius reikia "escapinti", ankščiau buvo naudojama funkcija mysql_real_escape_string tačiau dabar mums jos nebereikia!
cache_\get_field_content. Visose funkcijose nurodome tik eilutės indeksą ir stulpelio pavadinimą. Kodėl čia kitaip? Pažiūrėkime kokius parametrus turi funkcija:
native cache_get_field_content(row, const field_name[], destination[], connectionHandle = 1, max_len = sizeof(destination));
Mus domina paskutinis parametras. Mūsų atveju destination yra pInfo[ playerid ][ Vardas ]. Todėl gauname sizeof(pInfo[ playerid ][ Vardas ]) Viskas gerai, ar ne? Ne. Su enum'ais sizeof gražina visai ne tokį dydį kurio mes tikimės.
Lentelė duomenų bazėje.
- Prisijunkite prie phpMyAdmin(toliau pMA)
- Pasirinkite duomenų bazę arbą ją susikurkite
Toliau yra du keliai: įvykdyti užklausą kuri sukurs lentelę arba importuoti .sql failą.
Lentelės kūrimas su užklausa
-
Viršuje pasirinkite "SQL" kortelę
-
Įrašykite šią užklausą
CREATE TABLE IF NOT EXISTS `zaidejai` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `Vardas` varchar(24) NOT NULL, `Pinigai` int(11) NOT NULL, `Xp` int(11) NOT NULL, `Darbas` tinyint(11) NOT NULL, `Hp` float(11) NOT NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
-
Spauskite "Go"
Lentelės kūrimas su .sql failu
- Atsisiųskite lentelės .sql failą. Čia.
- pMA pasirinkite "Import" kortelę
- Pasirinkite atsisiųstą failą
- Spauskite "Go"
Ir viskas! Dabar jūsų serveris saugos šiek tiek informacijos apie žaidėją. Tikiuosi nieko nepamiršau... Jei ko nors trūksta, sakykite.
Hiwichi prieš 9 metus
Man vienam pribuginta quote? :D o šeip gerai, kad kuri pamokas ir dar su MySQL. Sukur kokia ir su BINI :D