Hallo Community, ich präsentiere euch hier ein Register-Login-System für die von BlueG gemachte MySQL R8+ (R26) Version.
In Version R8+ (R26) ist einiges anders, und was, dass wirst du gleich sehen.
Download Link: Link zum MySQL Plugin (Google - Code)
Die neuen Funktionen und die allgemeine Callbacks, die wir hier benutzen:
[tabmenu]
[tab='Callbacks']OnGameModeInit () // Nach main der nächstgeöffnete Callback nach dem Serverstart
OnGameModeExit () // Wird aufgerufen, wenn der Server beendet wird
OnPlayerConnect (playerid) // Spieler betritt den Server
OnPlayerDisconnect (playerid,reason) // Spieler verlässt den Server
OnDialogResponse (playerid, dialogid, response, listitem, inputtext[]) // Hier werden Dialogergebnisse gescriptet
OnPlayerRequestSpawn (playerid) // Wird aufgerufen, wenn der Spieler den Spawnbutton drückt
OnPlayerSpawn (playerid) // Wird aufgerufen, wenn der Spieler spawnt
[tab='Neue Funktionen']mysql_format (connectionHandle, output[], len, format[], {Float,_}:... ) // Ähnlich wie format(...), der Unterschied wird unten erklärt
mysql_tquery (connectionHandle, query[], callback[], format[], {Float,_}:... ) // Ähnlich wie mysql_function_query, bloß cache wird automatisch auf false gesetzt
mysql_function_query (connectionHandle, query[], bool:cache, callback[], format[], {Float,_}:... ) // das neue "mysql_query", das sogenannte threaded Querying
cache_get_field_content (row, const field_name[], dest[], connectionHandle = 1) // Gibt der Variable "dest", als String, den Inhalt des Feldes "field_name"
cache_get_field_content_int (row, const field_name[], connectionHandle = 1) // Sollte wie unten benutzt werden! - Gibt den Integer (Ganzzahl) des Feldes "field_name" in die Variable
cache_get_field_content_float (row, const field_name[], connectionHandle = 1) // Genauso wie bei cache_get_field_content_int, bloß diese als Float (Dezimalzahl)
[/tabmenu]
So kommen wir nun zu den Variablen, mit denen wir arbeiten werden:
// Optional kannst du natürlich, falls du das noch nicht gemacht hast, MAX_PLAYERS verringern
// Das ist äußerst resourcenschonender und schneller!
#undef MAX_PLAYERS
#define MAX_PLAYERS 5
// Die Zahl der max. Spieleranzahl musst du natürlich anpassen!
enum {
Dialog_Reg = 1, // Anpassen, falls notwendig
Dialog_Login
}
new mycon; // Würde ich verwenden, (mycon = MySQLConnection), um sicherzugehen (Siehe OnGameModeInit)
enum UserData
{
Username[MAX_PLAYER_NAME+1], // +1 wegen dem Null-Terminator '\0'
Geld, // Zum Test der Integerfunktion
Float:Leben, // Zum Test der Float Funktion
LastLogin[11], // Zum Test der Stringfunktion - XX/XX/XXXX + '\0' = 11 Zeichen
bool:Logged // Ist der Spieler eingeloggt?
}
new User[MAX_PLAYERS][UserData];
So wie du sehen kannst, gibt es keine Passwort Variable, wozu auch, die ist auch völlig unnötig!
Also, da wir die Variablen haben die wir brauchen, fangen wir mit den Callbacks an!
forward LoadPlayerDataSequence(playerid, l_step);
forward KickPlayer(playerid);
public OnGameModeInit()
{
mycon = mysql_connect("127.0.0.1","Username","Database","Passwort"); // Startet die Verbindung zur MySQL Datenbank (Angaben müssen angepasst werden!) (Homeserver muss 127.0.0.1, aufgrund des R26 Bugs(?))
// mycon müsste wenn es geklappt hat nun 1 sein, ansonsten seid ihr nicht richtig verbunden!
return 1;
}
public OnGameModeExit()
{
mysql_close(); // Das gibt es nach wie vor!
return 1;
}
// Kommen wir zum 3. Callback - Einer der Wichtigsten!
public OnPlayerConnect(playerid)
{
// Als allererstes sichern wir den Namen des Spielers
GetPlayerName(playerid,User[playerid][Username],MAX_PLAYER_NAME);
// Da wir diesen haben, kommt auch gleich der 1. Query!
new query[60 + MAX_PLAYER_NAME]; // Anpassen, je nachdem wie groß der Text in mysql_format ist! (Ohne %e)
mysql_format(mycon, query, sizeof(query), "SELECT COUNT(*) AS `count` FROM `User` WHERE `Name` = '%e'",User[playerid][Username]);
// So - Was habe ich hier gemacht?
// Folgendes: die Variable "query" hat nun den daneben eingegeben formatted Text bekommen
// Wir zählen die Anzahl der Einträge, die in der Tabelle "User" sind, wo der Name "Spielername einfügen" ist.
// Vielleicht fragt ihr euch, warum %e ? %e ist ein String, der speziell von mysql_format schon escaped wurde
// also spezielle Zeichen werden abgeändert, damit sie korrekt abgespeichert werden können
mysql_function_query(mycon, query, true, "LoadPlayerDataSequence","dd",playerid,1);
// So - Was habe ich hier gemacht?
// Ganz einfach, jetzt haben wir die Query rausgeschickt und dazu den Cache angeschalten!
// Die Antwort erhalten wir nun in LoadPlayerDataSequence
// Wie du unten sehen kannst enthält LoadPlayerDataSequence 2 Parameter, die, die ich oben angegeben habe!
return 1; // Das nicht vergessen ;)
}
// So Callback Nr. 4 - OnPlayerDisconnect
public OnPlayerDisconnect(playerid, reason)
{
if(User[playerid][Logged]) // Er soll nur gespeichert werden, wenn er auch aktiv war, also eingeloggt!
{
SavePlayerData(playerid); // Ich finde es extrem angenehm, es woanders zusammengefasst vorzufinden
}
ResetPlayerVariables(playerid); // Das Resetten kann eine lange Liste werden und auch das finde ich im unteren Bereich angenehmer
// ResetPlayerVariables sollte aber aus Sicherheitsgründen auch für nicht eingeloggte Spieler gelten
return 1;
}
// Callback Nr. 5 - OnPlayerSpawn
public OnPlayerSpawn(playerid)
{
// Da ich nicht weiß, wo ihr spawnen wollt, müsst ihr die Position selbst reinmachen!
// Das ist notwendig, da ihr sonst womöglich durch die Map fallt!
SetPlayerPos(playerid,??,??,??); // Anpassen!
return 1;
}
// Der vorletzte Callback - OnPlayerRequestSpawn
public OnPlayerRequestSpawn(playerid)
{
if(!User[playerid][Logged]) return 0; // Solange der Spieler nicht eingeloggt ist, soll er nicht spawnen können!
return 1; // Ermöglicht das Spawnen!
}
// Der Verwalter der Dialoge - OnDialogResponse
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch(dialogid)
{
case Dialog_Reg: // Nehmen wir zuerst den Register-Dialog
{
if(!response) // Wenn er den 2. Button (Abbrechen) gedrückt wurde
{
// Hier schicken wir den Spieler vom Server
SendClientMessage(playerid,0xFF0000FF,"Bitte benutzen Sie /q, um das Spiel zu beenden!");
SetTimerEx("KickPlayer",250,0,"i",playerid);
return 1;
}
// Kein else nötig, da wir oben returnt haben
if(!strlen(inputtext) || strlen(inputtext) < 3 || strlen(inputtext) > 25) // Für Anfänger (nehmen wir diese!)
// if(!inputtext[0] || !(3 <= strlen(inputtext) <= 25)) // Für Fortgeschrittene
{
// Da die Eingabe wegen Länge oder gar keiner Eingabe fehlgeschlagen ist, bitten wir ihn erneut um eine Eingabe, diesmal mit Hinweis (in Rot!)
ShowPlayerDialog(playerid, Dialog_Reg, DIALOG_STYLE_PASSWORD, "{00FF00}[Passwort Eingabe - Registrierung]","{FFFFFF}Bitte geben Sie Ihr gewünschtes Passwort ein!\n{FF0000}Bitte zwischen 3 & 25 Zeichen!","Register","Abbrechen");
return 1;
}
// Da das geklappt hat, lassen wir ihn auf den Server, davor aber legen wir einen Eintrag für ihn in der Datenbank ab
new query[128+MAX_PLAYER_NAME], year, month, day;
getdate(year, month, day);
format(query,sizeof(query),"%d/%d/%d",day,month,year); // Damit wir keine extra Variable noch extra erstellen müssen
mysql_format(mycon,query, sizeof(query), "INSERT INTO `User` (`Name`, `Passwort`, `Geld`, `Leben`, `LastLogin`) VALUES('%e','%e',0,100.0,'%e')", User[playerid][Username], inputtext, query);
mysql_tquery(mycon, query, "", ""); // Hier benutzen wir mysql_tquery, da wir keine Werte aus der Datenbank brauchen, sondern etwas reinschreiben
User[playerid][Logged] = true;
SpawnPlayer(playerid);
return 1;
// Register fertig!
}
case Dialog_Login:
{
if(!response) // Wenn er den 2. Button (Abbrechen) gedrückt wurde
{
// Hier schicken wir den Spieler vom Server
SendClientMessage(playerid,0xFF0000FF,"Bitte benutzen Sie /q, um das Spiel zu beenden!");
SetTimerEx("KickPlayer",250,0,"i",playerid);
return 1;
}
// Kein else nötig, da wir oben returnt haben
if(!strlen(inputtext) || strlen(inputtext) < 3 || strlen(inputtext) > 25) // Wie gesagt, Für Anfänger
// if(!inputtext[0] || !(3 <= strlen(inputtext) <= 25)) // Für Fortgeschrittene
{
// Da die Eingabe wegen Länge oder gar keiner Eingabe fehlgeschlagen ist, bitten wir ihn erneut um eine Eingabe, wieder mit Hinweis (in Rot!)
ShowPlayerDialog(playerid, Dialog_Login, DIALOG_STYLE_PASSWORD, "{00FF00}[Passwort Eingabe - Loginsequenz]","{FFFFFF}Bitte geben Sie Ihr Passwort ein!\{FF0000}Es hat mindestens 3 Zeichen!","Login","Abbrechen");
return 1;
}
// So hier müssen wir nun Abfragen ob das Passwort denn dasselbe ist!
new query[80 + MAX_PLAYER_NAME];
mysql_format(mycon, query, sizeof(query), "SELECT COUNT(*) AS `count` FROM `User` WHERE `Name` = '%e' AND `Passwort` = '%e'",User[playerid][Username],inputtext);
// Hier ganz einfach, wie beim anderen COUNT, wird hier auch gezählt, ob es diesen Eintrag, wo Name und Passwort gleich sind, gibt!
mysql_function_query(mycon, query, true, "LoadPlayerDataSequence","dd",playerid,2); // Step 2
// Das ganze schicken wir ab!
return 1;
}
}
return 1;
}
public LoadPlayerDataSequence(playerid, l_step)
{
switch(l_step) // Damit man nicht ewig if(...) else if(...) usw. schreiben muss
{
case 1:
{
// Jetzt gucken wir, ob es einen Eintrag gibt
new count = cache_get_field_content_int(0,"count"); // Hier holen wir uns den Integer als der "count", die wir temporär in MySQL erstellt haben und stecken sie in die Variable count
if(count) // Wenn count 1 oder höher ist (Keine Angst, höher als 1 kann sie nicht kommen)
{
// Der Eintrag existiert, also lassen wir den Spieler einloggen
ShowPlayerDialog(playerid, Dialog_Login, DIALOG_STYLE_PASSWORD, "{00FF00}[Passwort Eingabe - Loginsequenz]","{FFFFFF}Bitte geben Sie Ihr Passwort ein!","Login","Abbrechen");
return 1;
}
else
{
// Es gibt keinen Eintrag, also ist er neu und muss sich registrieren
ShowPlayerDialog(playerid, Dialog_Reg, DIALOG_STYLE_PASSWORD, "{00FF00}[Passwort eingabe - Registrierung]","{FFFFFF}Bitte geben Sie Ihr gewünschtes Passwort ein!","Register","Abbrechen");
return 1;
}
}
case 2:
{
// Jetzt gucken wir, ob es einen Eintrag gibt
new count = cache_get_field_content_int(0,"count");
if(count) // Wenn count 1 oder höher ist
{
// Der Eintrag existiert, also lassen wir den Spieler nun die Spielerdaten laden und danach den Spieler spawnen
new query[60 + MAX_PLAYER_NAME];
mysql_format(mycon,query, sizeof(query), "SELECT * FROM `User` WHERE `Name` = '%e'",User[playerid][Username]);
mysql_function_query(mycon,query,true,"LoadPlayerDataSequence","dd",playerid,3);
return 1;
}
else
{
// Es gibt keinen Eintrag, also ist das Passwort falsch
ShowPlayerDialog(playerid, Dialog_Login, DIALOG_STYLE_PASSWORD, "{00FF00}[Passwort Eingabe - Loginsequenz]","{FFFFFF}Bitte geben Sie Ihr richtiges Passwort ein!\{FF0000}Die vorherige Eingabe war falsch!","Login","Abbrechen");
return 1;
}
}
case 3:
{
// Wir laden nun die Spielerdaten
// Der Vorteil bei den cache-Funktionen ist, dass sie am Ende kein mysql_free_result brauchen sondern direkt danach den Cache leeren
User[playerid][Geld] = cache_get_field_content_int(0,"Geld");
User[playerid][Leben] = cache_get_field_content_float(0,"Leben");
cache_get_field_content(0,"LastLogin",User[playerid][LastLogin]);
User[playerid][Logged] = true;
SpawnPlayer(playerid);
return 1;
// Fertig!
}
default:
{
// Falls er hier auftauchen sollte, was eigentlich unwahrscheinlich ist, wird er aufgefordert zu reconnecten, und gleichzeitig gekickt!
SendClientMessage(playerid,0xFF0000FF,"Es könnte ein Bug aufgetreten sein! Bitte reconnecten Sie!");
SetTimerEx("KickPlayer",250,0,"i",playerid); // Wegen einem Bug von SA:MP muss der Kick nun per Timer ablaufen, damit die Nachricht auftaucht!
return 1;
}
}
return 1;
}
stock SavePlayerData(playerid)
{
// Hier speichern wir noch die Daten ab
new query[150 + MAX_PLAYER_NAME];
new day,month,year; getdate(year,month,day);
format(User[playerid][LastLogin],11,"%d/%d/%d",day,month,year);
mysql_format(mycon,query, sizeof(query), "UPDATE `User` SET `Geld` = %d,`Leben` = %.1f,`LastLogin` = '%e' WHERE `Name` = '%e'",User[playerid][Geld],User[playerid][Leben],User[playerid][LastLogin],User[playerid][Username]);
mysql_tquery(mycon,query,"","");
return 1;
}
stock ResetPlayerVariables(playerid)
{
// Und hier noch Resetten
for(new x; UserData:x < UserData; ++x) User[playerid][UserData:x] = 0; // Danke für den Tipp Prototype
return 1;
}
public KickPlayer(playerid)
return Kick(playerid);
Das Tutorial war lang, aber ich hoffe dafür sehr lehrreich und mit guten Erklärungen gefüllt!
Bei Fragen und evtl. Fehlern schreibt mich doch an, oder besser schreibt es hier in diesen Thread.
MfG NicoAiko