Tag Community,
Ich möchte euch heute gerne etwas zu einem Ressourcenschonenden Mysql Login/Registersystem basierend auf der Mysql Version R5 von BlueG zeigen.
Einige stock's wurden dabei von Maddin's Tutorial übernommen wie z.B CreateAccount etc.
Ich hoffe ich kann einigen von euch dabei helfen nicht all zu Ressourcenfressend die Speicherung und Ladung eines Spielers zu erstellen. (Bei Maddin 1 query Pro Varibale....)
Vieles wurde als Beispiel wiedergegeben !
Download hier : >>> KLICK <<<
Fangen wir mal an :
Zunächst sollte man ganz oben im Script die Standart Include welche bereits im Pawno Ordner dabei ist eintragen, dies geht mit einer Simplen Funktion.
#include <a_mysql> //Dabei wird die Include welche im Pawno/Include zu finden ist geladen.
#include <sscanf2> // Um die Include sscanf zu laden welchen wir später zum schonenden Laden des Spielers benötigen.
Um natürlich das ständige wechseln der Zugangsdaten für die Mysql Datenbank zu vermeiden wäre es vom Vorteil diese mit einem #define zu Definieren.
Wie z.b so :
#define My_Host "HOST" // Der Host
#define My_User "USERNAME" //Der Benutzername vom Mysql Account
#define My_Pass "PASSWORT" // Das Passwort um verbinden zu können
#define My_DB "DATENBANK NAME" //Der Name der Datenbank wo alles gespeichert wird.
/* Beispiel:
#define My_Host "loclahost"
#define My_User "samp1760"
#define My_Pass "12345"
#define My_DB "DB_samp1760"
*/
Um später keine Errors bei den Farben welche ich verwendet habe zu bekommen wäre es Hilfreich diese noch zu definieren.
#define C_ROT 0xFF000FFF
#define C_BLAU 0x1E00FFFF
Damit wir auch was zu Laden / Speichern von einem Spieler haben sollten wir einen enum dafür schreiben. Und danach mit einer Variable "bündeln".
enum PlayerDaten // Beschreibt den Enum Namen
{
pName[24],
pPasswort[128],
pLevel, //Eine Variable für das Level
pAdmin, // Eine Variable für das Adminlevel
Float:pX // Die X Koordinate
}
new PInfo[MAX_PLAYERS][PlayerDaten]; // Die Variable PInfo[playerid][pLevel/pAdmin/pX] damit werden wir die Spieler Daten Abfragen und hineinladen und speichern.
Ein Dialog darf bei einem Login/Register System natürlich auch nicht fehlen, daher müssen wir auch 2 Dialoge definieren.
Dies geht am besten in einer Enum Variante welche zusätzlich schonend für den Server ist. Weiteres zu Dialogen im Enum findest du hier : >>> KLICK <<<
enum // Ein unbezeichneter Enum für die Dialoge, ist auch nicht nötig
{
D_REGISTER, // Dialog Register, beliebig änderbar
D_LOGIN // Dialog Login, beliebig änderbar
}
Nun kommen wir zum Teil unter OnGameModeInit, dort lassen wir die Tabellen erstellen und wir Connecten gleichzeitig zur Datenbank.
Zuerst müssen wir zur Datenbank Connecten (by Maddin) um auch anschließend die Tabellen erstellen zu können. Für das Connecten zur Datenbank verwenden wir einen
kleinen stock um es einfacher zu machen.
C_T_D(); // Beliebiger Name für den Stock
// Der stock dazu (In keinen Callback schreiben !) :
stock C_T_D()
{
mysql_connect(My_Host, My_User, My_DB, My_Pass); // Eine Funktion von der Include diese ermöglich das Verbinden
if(mysql_ping() == 1) // Überprüft ob eine Verbindung exestiert..
{
print("||>>|| Die Mysql Verbindung konnte hergestellt werden ||<<||"); // Es wird etwas in die Konsole geschrieben.
return true;
}
else // Sollte keine Verbindung entstehen...
{
print("||>>|| Die Mysql Verbindung konnte nicht hergestellt werden. Versuchen es erneut !");
mysql_connect(My_Host, My_User, My_DB, My_Pass); //.... Versuchen wir es erneut
if(mysql_ping() == 1) //... Wenn es dann klappt
{
print("||>>|| Die Verbindung konnte hergestellt werden ||<<||");
return true;
}
else // Wenn es beim 2. Mal wieder nicht klappt
{
print("||>>|| Die Verbindung konnte nicht hergestellt werden, Server fährt herunter ! ||<<||");
SendRconCommand("exit"); // Server fährt herunter mittels eines RCON Commands.
return true;
}
}
}
Das Tabellen erstellen ist im Prinzip auch ganz einfach, die Vorteile dabei sind man muss nicht extra eine neue Datei schreiben und diese Hochladen.
Jedoch sollte dabei ein Grundwissen vorhanden sein (Der PAWN Code kommt unter OnGameModeInit). Da ich dabei nicht genau drauf ein gehen möchte, gibt es hier auch was schönen : >>> KLICK <<<
mysql_query("CREATE TABLE IF NOT EXISTS `accounts` (`id` int(11) NOT NULL AUTO_INCREMENT, `Name` varchar(24),`Passwort` varchar(24),`Level` int(11),`Admin` int(11), `pX` float,PRIMARY KEY (`id`)) ENGINE=MyISAM DEFAULT CHARSET=latin1;"); // Ein query wird gesendet.
Um dem Spieler beim Connecten den Dialog für das Registrieren/Einloggen zuzeigen verwenden wir den Callback OnPlayerRequestClass um auch Skin Auswahl Buggs zu vermeiden.
public OnPlayerRequestClass(playerid, classid)
{
if(GetPVarInt(playerid,"logged") == 0) // Ein PVar welchen wir später beim Einloggen / Registrieren auf 1 Setzen.
{
if(mysql_CheckAccount(playerid) == 0) // Dies ist ein stock denn wir schreiben, er überprüft ob ein Account exestiert... Wenn nicht :
{
ShowPlayerDialog(playerid,D_REGISTER,DIALOG_STYLE_PASSWORD,"Tut-Register","Bist du dir sicher das du dich registrieren möchtest ?\nWenn ja, gib ein Passwort an !","Register","Abbrechen");
}
else if(mysql_CheckAccount(playerid) == 1) // ... Sollte ein Account exestieren :
{
ShowPlayerDialog(playerid,D_LOGIN,DIALOG_STYLE_PASSWORD,"Tut-Login","Bitte logge dich ein !","Login","Abbrechen");
}
}
return 1;
}
// Der stock mysql_CheckAccount ( In keinen Callback schreiben ! ) :
stock mysql_CheckAccount(playerid)
{
new query[128],name[MAX_PLAYER_NAME],count; //Neuer Array, Um den Namen zu ermitteln, Und eine Varibale fürs wiedergeben des wertes
GetPlayerName(playerid,name,MAX_PLAYER_NAME); // Wir holen seinen Namen
mysql_real_escape_string(name,name);
format(query,sizeof(query),"SELECT * FROM `accounts` WHERE `Name` = '%s'",name); // Wir holen uns den Account sofern er exestiert
mysql_query(query);
mysql_store_result();
count = mysql_num_rows(); // Wir speichern den Wert ob dieser Exestiert in der Variable count
mysql_free_result();
return count; // Wir geben den Wert wieder...
}
So, gehen wir nun weiter zum nächsten Callback undzwar OnDialogResponse. In diesem werden wir schreiben was der Server zu tun hat wenn der Spieler sich registriert oder Einloggt.
Um wieder etwas schonender umzugehen verwenden wir bei der Dialog ID die Funktion switch statt if(dialogid == ....)
public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
{
switch(dialogid) // Wir führen dialogid so damit wir diese im case verwenden können (Übersichtlicher und schonender )
{
case D_REGISTER:
{
if(response)
{
if(strlen(inputtext) == 0) // Wenn der Spieler kein Passwort angibt...
{
ShowPlayerDialog(playerid,D_REGISTER,DIALOG_STYLE_PASSWORD,"Tut Register","FEHLER: PASSWORT EINGEBEN !","Register","Abbrechen"); //... Kommt dieser DIalog
}
else // Wenn der Spieler aber ein PAsswort angibt
{
CreateAccount(playerid,inputtext); // Führen wir den stock CreateAccount aus.
SetPVarInt(playerid,"logged",1); // Wir setzen im den PVar logged auf 1
SpawnPlayer(playerid); // Und spawnen den Spieler anschließend.
return 1;
}
}
else Kick(playerid); // Wenn der Spieler im Dialog auf Abbrechen klickt, wird er gekickt.
}
case D_LOGIN: // Dialog Login
{
if(response)
{
if(strlen(inputtext) == 0) // Wenn er nichts eingibt...
{
ShowPlayerDialog(playerid,D_LOGIN,DIALOG_STYLE_PASSWORD,"Tut Login","FEHLER: PASSWORT EINGEBEN !","Login","Abbrechen"); //... zeigen wir ihm den Dialog
}
else // Wenn ein Passwort eingegeben wurde...
{
if(!strcmp(inputtext, mysql_ReturnPasswort(SpielerName(playerid)), true)) //... überprüfen wir das Passwort mittels einen stock welchen wir schreiben werden.
{
SetPVarInt(playerid,"logged",1); // Oben erklärt
LoadPlayer(playerid); // Wir laden den Spieler mittels einem Stock auf schonende Art und Weise
SpawnPlayer(playerid); //Oben erklärt
return 1;
}
else Kick(playerid);
}
}
}
}
return 1;
}
// Die stocks diese jetzt verwendet werden ausser LoadPlayer :
stock CreateAccount(playerid,pass[])
{
new query[256],name[MAX_PLAYER_NAME]; // Neuer Array, eine Variable für den Namen
GetPlayerName(playerid,name,MAX_PLAYER_NAME); // Wir holen uns den Namen
mysql_real_escape_string(name,name);
mysql_real_escape_string(pass,pass);
format(query,sizeof(query),"INSERT INTO `accounts` (`Name`, `Passwort`) VALUES ('%s','%s')",name,pass); // Wir schreiben den Namen und das Passwort in die Mysql Datenbank
mysql_query(query); // Wir senden den query an die Datenbank.
return true;
}
stock mysql_ReturnPasswort(Name[])
{
new query[130],get[130]; // Neuer Array, Array für das Passwort
mysql_real_escape_string(Name,Name);
format(query,sizeof(query),"SELECT `passwort` FROM `accounts` WHERE = '%s'",Name); // Wir schreiben den query wo wir das Passwort holen.
mysql_query(query); // Senden diesen an die Datenbank
mysql_store_result();
mysql_fetch_row(get); // Speichert das Passwort in die Variable get
mysql_free_result();
return get;
}
So nun kommen wir zu dem wichtigsten, wo viele User Probleme damit haben, undzwar die Ressourcenschonende Speicherung bzw. Ladung des Spielers. Viele Anfänger in Sachen Mysql
verwenden da die stocks von Maddin's Tutorial (mysql_GetInt , mysql_SetInt, etc. ) diese verwenden pro Variable einen query der gesendet wird, das heißt bei 80 Variablen wird
80 Mal etwas an die Datenbank gesendet und daraus etwas geholt das heißt 160 Mal wird zwischen einem Spieler hin und her Connectet.
Zuerst mal das Speichern des Spieler mithilfe von einem query. In einem query könnt ihr genug Variablen unterbringen, wenn es aber ein Godfather ist sind es natürlich viele Variablen, da muss
man z.B drei oder vier querys schreiben.
//Zunächst folgendes unter OnPlayerDisconnect schreiben :
SpielerSpeichern(playerid); // Dies ruft den folgenden stock auf....
//____________________________________
stock SpielerSpeichern(playerid)
{
if(IsPlayerConnected(playerid) && !IsPlayerNPC(playerid)) // Sofern der Spieler eingelogg ist bzw. im Spielt ist und kein NPC / BOT ist.
{
if(GetPVarInt(playerid,"logged") == 1) // Ob er eingeloggt ist, hätten wir diesen PVar nicht und der Spieler würde währen des Einloggen's Disconnecten wo die Variablen von ihm noch nicht geladen wurden, so wäre danach alles auf 0
{
new query[500]; // Neuer query || TIPP : Die Länge eines Querys lässt sich mit Notepad++ Sehr leicht ermitteln einfach den Input hineinschreiben und ablesen wieviel Zeichen dieser hat.
format(query,sizeof(query),"UPDATE `accounts` SET `Level` = '%d', `Admin` = '%d', `pX` = '%f' WHERE `Name` = '%s'",PInfo[playerid][pLevel],PInfo[playerid][pAdmin],PInfo[playerid][pX],SpielerName(playerid));
// UPDATE `accounts` = Wir erneuern die Tabelle Accounts
// SET `Level` = '%d' .... Wir setzten dort das Level %d als Integer Bei Float's ist dies %f und bei Namen / Strings %s
// WHERE `Name` = '%s' = Wo der Name dem Spieler Namen entspricht.
mysql_query(query); // Wir senden ab
}
}
return 1;
}
So jetzt kommen wir zum stock der den Spieler Laden lässt, dabei verwenden viele die bekannten stocks mysql_GetInt etc...
Wir machen dies wiederum auf schonende weiße.
stock LoadPlayer(playerid)
{
if(IsPlayerConnected(playerid) && !IsPlayerNPC(playerid)) // Ob der Spieler InGame ist und kein NPC / Bot
{
new query[250],str[500];
format(query,sizeof(query),"SELECT * FROM `accounts` WHERE `Name` = '%s'",SpielerName(playerid));
mysql_query(query);
mysql_store_result();
while(mysql_fetch_row(str))
{
sscanf(str, "e<p<|>{i}s[24]s[128]iif>",PInfo[playerid]);
mysql_free_result();
}
}
return 1;
}
Nun zu guter letzt fehtl uns nur noch ein stock, undzwar der stock für den Namen des Spielers:
stock SpielerName(playerid)
{
new name[MAX_PLAYER_NAME];
GetPlayerName(name,sizeof(name));
return name;
}
So das wars jetzt von mir, Ich hoffe euch hat das Tutorial gefallen und geholfen.
Solltet ihr Fragen haben, so fragt hier in diesem Thread.
Solltet ihr Fehler sehen so sagt dies bitte, damit ich es ausbessern kann.
MFG
[DT]Nightstr3am
//edit stock hinzugefügt
//edit sscanf Lade Spieler hinzugefügt