Guten Abend,
Einleitung:
Mein Name lautet MrPawn ( Michael ).
Heute biete ich euch ein Simples Tutorial über ein Anmeldung´s System mit der Version R38.
Aktuell gibt es auch die Version R39, aber das Prinzip wird sich nicht viel verändert haben.
Bei dem Update von R38 - 39 wurden höchstens ein paar Korigierungen vorgenommen.
So nun wollen wir aber mal anfangen mit der Anleitung für ein Simples Anmeldungs System.
Ob es 100% Anfängertäuglich ist stellt sich zu einem späteren Zeitpunkt heraus.
Voraussetzungen:
Bevor wir mit diesem System anfangen wollen, müssen wir zuerst einmal wissen was ich alles dafür brauchen.
Die Voraussetzungen werde ich nun auflisten.
- Das MySQL Include in der Version r38/39.
- Das jeweilige bereitgestellte MySQL Plugin in der Version R38.
- Einen beliebigen Windows/Linux SA-MP Server.
- Eine MySQL Datenbank, zur Testzwecken reicht eine ganz normale Lokale XAMPP Datenbank.
- Leichte Grundkenntnisse.
Installation:
Was wir zuerst machen werden, ist folgendes: Da ich ja das ganze auf einen Windows Server laufen lasse, werde ich nun die libmysql.dll in die Hauptverzeichnis vom Server ziehen.
Das gleiche machen wir mit der mysql.dll:
Nur bei ihr sieht das ganze anders aus:
Wir dürfen nähmlich das Plugin nicht in das Hauptverzeichnis klatschen.
Sondern müssen die mysql.dll in den plugins Ordner verschieben.
Dann muss die .inc Datei in /pawno/include/ verschoben werden.
Zuletzt muss das ganze einfach nur bei der server.cfg unter "plugins" enzutragen.
Wie man die XAMPP Datenbank Lokal hosten kann , werd ich allerdings nicht zeigen.
Verbindungsaufbau zu der MySQL Datenbank:
Da R38 immer nach einer ConnectingHandle fragen wird, werden wir uns eine Variable erstellen.
Das sollte weiter oben im Script erfolgen.
new Handle;
Dann müssen wir überlegen , aber welchem Zeitpunkt soll eine Verbindung hergestellt werden.
Das ist einfach zu beantworten.
Wenn der Server startet, soll eine Verbindung aufgebaut werden.
Wo kann man den Start sozusagen festlegen, das jeweilige Callback lautet: OnGameModeInit.
Aber voher um es leichter zu machen, erstellen wir uns ganz normal definierungen für die Datenbank.
Die können wir beliebig nennen, solange sie auf einen String zuweisen, sprich: ""
#define MYSQL_HOST "127.0.0.1"
#define MYSQL_USER "root"
#define MYSQL_DATA "tutorialdb"
#define MYSQL_PASS ""
So nun können wir beim "Serverstart Callback" die Verbindung versuchen herzustellen.
Dazu müssen wir die Funktion mysql_connect anwenden, auf diesen Connect müssen wir gleichzeitig unsere erstellte Variable "Handle" zuweisen.
Wie das gehen würde sehr ihr hier:
http://wiki.sa-mp.com/wiki/MySQL/R33#mysql_connect
public OnGameModeInit()
{
Handle = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_DATA, MYSQL_PASS);
return 1;
}
So aber am besten sollte man sich 2 Sachen vergewissern, 1. Das die Verbindung steht, 2. Das der Verlauf geloggt wird.
Um zu wissen, das die Verbindung steht müssen wir mit der Funktion mysql_errno Arbeiten.
http://wiki.sa-mp.com/wiki/MySQL/R33#mysql_errno
Verbindungstest:
public OnGameModeInit()
{
Handle = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_DATA, MYSQL_PASS);
if(mysql_errno() < 1)print("MySQL: Die Verbindung wurde erfolgreich hergestellt."); else print("MySQL: Die Verbindung zur MySQL Datenbank konnte nicht hergestellt werden | Der Server wird nun heruntergefahren."), SendRconCommand("exit");
return 1;
}
So sind wie auf der sicheren Seite.
Jetzt müssen wir dafür sorgen das ganze auch geloggt wird, deswegen Arbeiten wir mit der Funktion mysql_log.
http://wiki.sa-mp.com/wiki/MySQL/R33#mysql_log
Es gibt 5 Verschiedene Arten von Logs.
Typen:
LOG_NONE Logs absolutely nothing.
LOG_ERROR Logs errors.
LOG_WARNING Logs warnings.
LOG_DEBUG Logs debug messages.
LOG_ALL Logs everything.
Da wir nur die mal die ganze LOG im Blick haben, nehmen wir Type 5.
Das ganze wenden wir nun so an:
public OnGameModeInit()
{
Handle = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_DATA, MYSQL_PASS);
if(mysql_errno() < 1)print("MySQL: Die Verbindung wurde erfolgreich hergestellt."),mysql_log(LOG_ALL); else print("MySQL: Die Verbindung zur MySQL Datenbank konnte nicht hergestellt werden | Der Server wird nun heruntergefahren."), SendRconCommand("exit");
return 1;
}
So, Leute somit währe der Verbindungsaufbau vollbracht.
Query:
Jetzt werden wir mal mit Querys Arbeiten.
Ein Query ist sozusagen eine Abfrage, die aus der Datenbank erfolgt.
Wir werden jetzt eine Abfrage starten, ob ein Eintrag mit unserem namen in der Datenbank existiert.
Wir gehen also zum Callback was aufgerufen sobald ein Spieler auf den Server connected.
Dieses Callback nennt sich: OnPlayerConnect
public OnPlayerConnect(playerid)
{
return 1;
}
So nun erstellen wir uns einen neuen String, namens "query" ( muss nicht umbedingt sein, so mache ich das halt. )
public OnPlayerConnect(playerid)
{
new query[256];
return 1;
}
So nun müssen wir den Query/String befüllen lassen, sogesehen auch formatieren.
Das machen wir mit der Funktion format
format(query, sizeof(query), "..", ..);
So nun müssen wir auch wissen welchen befehl wie der abfrage für die Datenbank zuweisen sollen.
Aber was auch ganz wichtig ist, MySQL Befehle werden "IMMER!" Groß geschrieben.
Mit dem Command SELECT können wir von einer Tabele eine jeweilge Spalte durchsuchen nach werten,strings.
Wir müssen jeweils auch mit FROM & WHERE arbeiten , damit die DB auch weiß , was & wo sie suchen soll.
Ich mache hiermal einen beispiel code:
QUERY:
format(query, sizeof(query), "SELECT * FROM user WHERE username='%s'", ..);
So zuerst einmal muss das nicht user heißen sondern kann beliebig heißen, es muss halt nur in der Datenbank erstellen werden.
Dann müssen wir eine Variable mit dem jeweiligen Spielernamen füllen lassen. Was auch gehen würde wäre man sich gleich dafür eine Funktion erstellen würde.
Ich zeige mal beide beispiele:
1. Variante:
public OnPlayerConnect(playerid)
{
new query[256], name[24];
GetPlayerName(playerid, name, 24);
format(query, sizeof(query), "SELECT * FROM user WHERE username='%s'", name);
return 1;
}
2. Variante:
public OnPlayerConnect(playerid)
{
new query[256];
format(query, sizeof(query), "SELECT * FROM user WHERE username='%s'", Spielername(playerid));
return 1;
}
stock Spielername(playerid)
{
new name[24];
GetPlayerName(playerid, name, 24);
return name;
}
Aber wenn wir das so lassen würden, dann würde es nicht ganz gut Klappen.
Aus dem einfachen Grund, das MySQL wenn es schon ein String ist, einen escaped.
Deswegen arbeiten wir mit der Funktion mysql_escape_string.
http://wiki.sa-mp.com/wiki/MySQL/R33#mysql_escape_string
Dies ist nicht wirklich schwer.
Ich zeigs wieder mit 2 Varianten:
1. Variante:
mysql_escape_string(name, name);
2. Variante:
mysql_escape_string(Spielername(playerid), Spielername(playerid));
public OnPlayerConnect(playerid)
{
new query[256];
mysql_escape_string(Spielername(playerid), Spielername(playerid));
format(query, sizeof(query), "SELECT * FROM user WHERE username='%s'", Spielername(playerid));
return 1;
}
So nun müssen wir den Query auch absenden, dass machen wir mit der Funktion mysql_tquery
http://wiki.sa-mp.com/wiki/MySQL/R33#mysql_tquery
public OnPlayerConnect(playerid)
{
new query[256];
mysql_escape_string(Spielername(playerid), Spielername(playerid));
format(query, sizeof(query), "SELECT * FROM user WHERE username='%s'", Spielername(playerid));
mysql_tquery(Handle, query, "UserCheck", "i", playerid);
return 1;
}
So ich denke, Handle & Query lassen sich von selbst erklären aber nicht "UserCheck, i & playerid".
UserCheck wird als Callback verwendet (forward & public).
Das ist i steht für Integer.
Auf deutsch übersetzt ganzzahlige werte.
Und i wird dann halt mit playerid gefüllt.
So nun erstellen wir uns das Callback UserCheck, INFO: Kann wieder beliebig genannt werden.
forward UserCheck(playerid);
public UserCheck(playerid)
{
}
So nun müssen wir wissen, wie wir die Ergebnisse fragen, das ist Einfach. Wir müssen mit der Funktion cache_get_data 2 Variablen füllen lassen, und die einfach er 'if' frage prüfen.
Dazu erstellen wir auch noch weit oben im Script 2 Dialoge
#define DIALOG_REGISTER 1
#define DIALOG_LOGIN 2
http://wiki.sa-mp.com/wiki/MySQL/R33#cache_get_data
forward UserCheck(playerid);
public UserCheck(playerid)
{
new num_rows, num_fields;
cache_get_data(num_rows, num_fields, Handle);
if(num_rows == 0)
{
//Register..
ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Anmeldung", "Es wurde kein Account unter diesem namen gefunden!", "Anmelden", "Abbrechen");
}
else
{
//Login..
ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Anmeldung", "Es wurde ein Account unter diesem namen gefunden!", "Anmelden", "Abbrechen");
}
}
So nun müssen wir zum dem Callback was für die Antwort, es eines Buttons von einem Dialog ist. Eine sogenannte Response.
Das Callback nennt sich OnDialogResponse
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
return 1;
}
Da fragen wir erstmal die jeweilige DialogID ab.
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
new query[256], key[50];
if(dialogid == DIALOG_REGISTER)
{
if(!response)
{
return Kick(playerid);
}
if(!strlen(inputtext) < 4)return ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Anmeldung", "Es wurde kein Account unter diesem namen gefunden!", "Anmelden", "Abbrechen");
format(key, 50, "%s", inputtext);
mysql_escape_string(Spielername(playerid), Spielername(playerid)), mysql_escape_string(key, key);
format(query, sizeof(query), "INSERT INTO (username, passwort) VALUES ('%s',MD5('%s')", Spielername(playerid), key);
mysql_tquery(Handle, query);
SendClientMessage(playerid, -1, "Dein Account wurde erstellt.");
GivePlayerMoney(playerid, 50000), SetPlayerScore(playerid, 10);
}
return 1;
}
Jetzt fragen sich bestimmt die neulinge, wieso ich MD5('..') gemacht habe.
MD5 ist eine Verschlüsseung für string. MySQL liefert die Funktion schon mit sich.
So jetzt kümmern wir uns die Login Funktion:
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
if(dialogid == DIALOG_LOGIN)
{
if(!response)
{
return Kick(playerid);
}
format(key, 50, "%s", inputtext);
mysql_escape_string(Spielername(playerid), Spielername(playerid)), mysql_escape_string(key, key);
format(query, sizeof(query), "SELECT * FROM user WHERE username='%s' AND password='%s'", Spielername(playerid), key);
mysql_tquery(Handle, query, "OnPasswordResponse", "i", playerid);
}
return 1;
}
forward OnPasswordResponse(playerid);
public OnPasswordResponse(playerid)
{
new num_fields, num_rows;
cache_get_data(num_rows, num_fields);
if(num_rows == 0)
{
//Passwort falsch..
SendClientMessage(playerid, -1, "Das Passwort ist inkorrekt.");
ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Anmeldung", "Es wurde ein Account unter diesem namen gefunden!", "Anmelden", "Abbrechen");
}
else
{
//Passwort richtig..
SendClientMessage(playerid, -1, "Erfolgreich eingeloggt.");
SpielerInfo[playerid][pEingeloggt] = true;
LoadAccount(playerid);
SpawnPlayer(playerid);
}
return 1;
}
Wir erstellen nun wieder weiter oben im Script 1 Enum + 1 Variable
enum PD {
pEingeloggt,
pGeld,
pLevel,
};
new SpielerInfo[MAX_PLAYERS][PD];
stock LoadAccount(playerid)
{
SpielerInfo[playerid][pEingeloggt] = true;
SpielerInfo[playerid][pGeld] = cache_get_field_content_int(0, "Geld", Handle), GivePlayerMoney(playerid, SpielerInfo[playerid][pGeld]);
SpielerInfo[playerid][pLevel] = cache_get_field_content_int(0, "Level", Handle), SetPlayerScore(playerid, SpielerInfo[playerid][pLevel]);
return 1;
}
So, nun kommen wir fast zum Ende. Am Ende wirkt es vielleicht ein bisschen wie c & p aber wenn die Leute dieses Tutorial hier sehen und das wirklich lernen wollen, dann werden sie schon nicht nur c&p machen.
Ab zur SpielerSpeichern Funktion.
Nun müssen wir überlegen, wo soll die hin? Das ist Simpel, wie alles andere hier auch:
public OnPlayerDisconnect(playerid)
{
SpielerSpeichern(playerid);
return 1;
}
stock SpielerSpeichern(playerid)
{
if(SpielerInfo[playerid][pEingeloggt] == false)return 1;
mysql_escape_string(Spielername(playerid), Spielername(playerid));
format(query, sizeof(query), "UPDATE user SET Geld='%i',Level='%i' WHERE username='%s'", GetPlayerMoney(playerid), GetPlayerScore(playerid), Spielername(playerid));
mysql_tquery(Handle, query);
return 1;
}
So Leute das war es eigentlich im großen & ganzen.
DOWNLOADS
Komplettes MySQL Pack ( R38 ) Windows: ► Direkt Download
Komplettes MySQL Pack ( R38 ) Windows: ► File Upload (Link Down, wird erneuert)
Komplettes MySQL Pack ( R38 ) Windows: ► Dropbox
Mit Freundlichen Grüßen,
MrPawn