[jTuT] MySQL R41-4 (Installation, XAMPP, Verwendung, Bedienung & Registrations-System)

Wichtiger Hinweis: Bitte ändert nicht manuell die Schriftfarbe auf schwarz sondern belasst es bei der Standardeinstellung. Somit tragt ihr dazu bei dass euer Text auch bei Verwendung unseren dunklen Forenstils noch lesbar ist!

Tipp: Ihr wollt längere Codeausschnitte oder Logfiles bereitstellen? Benutzt unseren eigenen PasteBin-Dienst Link
  • [jTuT] MySQL R41-4
    Installation, XAMPP, Verwendung, Bedienung & Registrations-System





    Hallo Zusammen,


    dieses Tutorial beschreibt die Installation sowie die Anwendung des aktuellsten MySQL Plugins für SA-MP (R41-4 / Stand 10/2017). Es baut in den Grundformen auf maddin's MySQL Tutorial auf, welches allerdings nicht mehr aktuell ist. Gerne kann dieser Thread auch für Fragen bezüglich dem Plugin oder den Codes verwendet werden.




    Inhaltsverzeichnis

    • Voraussetzungen
    • Kurzer Überblick
    • Nützliches Vorwissen
    • Aufsetzen der Datenbank in Windows mit XAMPP und phpMyAdmin
    • SA-MP Server und MySQL Datenbank verbinden
    • Register & Login System
    • Hinweise und Fehlerbehebung
    • Fußnoten
    • Für die Faulen unter uns




    1.) Voraussetzungen

    • Einzigste Voraussetzung für dieses Tutorial ist ein vorhandener Windows PC mit dem aktuellsten SA-MP Server, beziehungsweise ein SA-MP Server mit MySQL Anbindung bei einem Hoster.




    2.) Kurzer Überblick

    • Dieses Tutorial beschreibt und erklärt die Installation, Aufsetzung und Anwendung des neusten MySQL Plugins. Außerdem wird der Aufbau der Tabelle in der Datenbank, sowie das Laden und das Speichern von Spielerstatistiken anhand von Beispielen erklärt. Am Ende haben wir ein komplett funktionsfähiges Registrations-System welches auf dem MySQL Plugin Version R41-4 basiert. Falls Du das Plugin noch nicht hast, lade es Dir bitte jetzt herunter und füge es entsprechend in Deinen Server ein.
      Es ist zu beachten, dass die Codes ganz bewusst mit englischen Variablennamen versehen worden sind, da dies der Einheitlichkeit und Allgemeinverständlichkeit eines Codes dient. Kommentare habe ich auf deutsch gehalten.




    3.) Nützliches Vorwissen

    • Was ist MySQL überhaupt?
      MySQL steht als Abkürzung für My Structured Query Language, was soviel wie "strukturierte Abfragesprache" heißt. Das ganze wird strukturiert genannt, weil es, anders als bei Dateisystemen, sehr übersichtlich aufgebaut ist, und alle Daten an einem Punkt sind, nämlich in der Datenbank. Man hat nicht tausende Dateien, die man zu verwalten hat, sondern genau eine Datenbank, in der bestmöglichst alles steht. Diese Datenbanken sind unbegrenzt* groß, es können als so viele Datensätze und Daten wie man will in der Datenbank gespeichert werden.
    • Was sind die Vorteile davon? Gibt es auch Nachteile?
      Der wohl größte Vorteil liegt in der Geschwindigkeit, in der sehr viele Daten auf einmal ausgewertet werden können. Beim abfragen von einzelnen Daten ist MySQL meist langsamer als Dateisysteme, sobald man aber viele Daten zu verwalten hat, dann lohnt sich MySQL auf jeden Fall.
      Außerdem bietet sich mit einer MySQL Datenbank die Möglichkeit, die Daten mit einer Website zu verknüpfen, das geht mit Dateien keineswegs so leicht.
      Natürlich gibt es auch Nachteile, dort ist besonders der hervorzuheben, dass es oft schwieriger für Anfänger ist, MySQL zu verstehen. Aber genau um diesem Nachteil entgegen zu wirken gibt es dieses Tutorial, damit wir gemeinsam auf die wichtigsten Dinge eingehen können, damit es anschließend ein Leichtes für Dich ist, MySQL zu verstehen.
    • Vergleich zwischen einer Datei (dini) und einer MySQL Tabelle.
      In einer Datei sieht es bekanntermaßen oft so aus:
      ->Datei: /users/Jeffry.sav

      Code
      password=D5H)12DAI?4XST21/&DL1
      level=1
      money=1000
      kills=14
      deaths=3



      ->Datei: /users/Horst.sav

      Code
      password=ZIJ(/5SGJ!$96T6SD§F43
      level=5
      money=99
      kills=19
      deaths=96



      Eine MySQL Tabelle sieht dann bei entsprechenden Daten so aus:


      Wie Du siehst, besteht eine MySQL Tabelle - wer hätte es gedacht - aus einer Tabelle, in der die Daten stehen. Im Beispiel der Tabelle sind dort alle Benutzer untereinander aufgeführt, es muss nicht für jeden Benutzer eine eigene Datei erstellt werden. Das hat vor allem bei Änderungen oder Server-Umzügen extreme Vorteile und erleichtert die Arbeit ungemein.

    • Externer Zugriff auf MySQL Datenbanken
      Ein externer Zugriff auf einer MySQL Datenbank (SA-MP Server auf Maschine A, MySQL Datenbank auf Maschine B) ist möglich, allerdings ist ein solches Vorgehen nicht zu empfehlen. Warum? Ganz einfach: Die Daten können während der Übertragung abgefangen werden. Natürlich sind das meist keine sensiblen Daten, aber dennoch sollte man sich darüber im Klaren sein. Außerdem, und das ist der ausschlaggebendere Punkt, dauert die Übertragung natürlich seine Zeit. Muss der MySQL Server aus den USA die Daten ins Rechenzentrum nach Frankfurt schicken, dann dauert das einen kurzen Moment (Millisekunden / Sekunden). Kommt es nun mal vor, dass der MySQL Server nicht erreichbar ist, dann hat man ein echtes Problem, denn das bedeutet, dass keine Daten mehr gelesen beziehungsweise geschrieben werden können, was oft zu Fehlern oder gar zum Server-Absturz führt.
      Daher ist es immer empfohlen, die Datenbank auf dem gleichen Server zu betreiben, auf dem auch der SA-MP Server liegt. Dies ist bei den großen Hostern ohnehin automatisch so geregelt.
    • Sammlung aller MySQL-Befehle
      Ich verweise hier auf die Sammlung aller SQL-Statements, die von MySQL unterstützt werden. In dieser Sammlung befinden sich auch Erklärungen und Beispiele zu den jeweiligen Befehlen.
      http://dev.mysql.com/doc/refman/5.7/en/sql-syntax.html
      Diese Liste musst Du natürlich nicht auswendig können. Es reicht zu wissen, dass diese Liste existiert, falls Du irgendwann etwas spezielles nachschauen möchtest.
      Ebenso verweise ich hier noch auf den aktuellen Wiki Eintrag für das MySQL Plugin: http://wiki.sa-mp.com/wiki/MySQL/R40
    • Verstehen der SQL Syntax
      Falls Du Probleme damit hast, die SQL Syntax zu verstehen, oder vor diesem Tutorial die SQL-Grundkenntnisse anhand von einfachen Beispielen lernen möchtest, empfehlen ich Dir diese Seite:
      http://www.w3schools.com/sql/default.asp
      Dort sind die wichtigsten Dinge, bis hin zu etwas komplexeren Themen, sehr gut und verständlich erklärt. Außerdem können die erklärten Funktionen direkt im Browser von Dir selbst ausprobiert werden.
      Ich kann in diesem Tutorial nicht die komplette SQL Syntax erklären, daher der Verweis auf diese wirklich gute Seite. Falls Du Fragen dazu hast, darfst Du diese natürlich trotzdem gerne hier stellen.




    4.) Aufsetzen der Datenbank in Windows mit XAMPP und phpMyAdmin

    • Installation von XAMPP
      Die Installation von XAMPP ist nur notwendig, wenn Du die MySQL Datenbank auf Deinem Windows-Rechner aufsetzen willst. Liegt der SA-MP Server bei einem Hoster, dann ist dieser Schritt nicht notwendig, da die entsprechenden Mittel in den meisten Fällen bereits gegeben sind. Sollte dies nicht der Fall sein, setze Dich einfach mit Deinem Host in Verbindung, der Support wird Dir bestimmt helfen.
      Gehen wir nun also davon aus, dass Du die MySQL Datenbank auf Deinem Rechner installieren willst. Lade hierzu XAMPP herunter. Den Download findet man auf der offiziellen Seite: Download
      Nach erfolgreichem Download findest Du in Deinem Download-Verzeichnis ein Installer-Paket um die Installation von XAMPP zu starten. Starte diesen Installer mit einem Doppelklick.


      In einigen Fällen werden Warnmeldungen wie folgende angezeigt. Diese können einfach übersprungen werden.


      Als erstes erscheint das Hauptfenster des Installers. Hier genügt es mit einem Klick auf "Next" fortzufahren.


      Im darauf folgenden Schritt kannst Du auswählen, welche Komponenten mit XAMPP auf Deinem Rechner installiert werden. Hier empfiehlt es sich, alle Komponenten auszuwählen und mit "Next" zu bestätigen.


      Jetzt musst Du den Pfad angeben, auf dem XAMPP installiert werden soll. Grundsätzlich empfiehlt es sich hier, den Standard-Pfad beizubehalten und mit "Next" zum nächsten Schritt zu springen.


      Natürlich will Dich auch dieser Installer freundlicherweise auf andere Programme aufmerksam machen. Darauf verzichtest Du empfehlenswerterweise, indem Du den Haken in der Checbox entfernst und dies mit "Next" bestätigst.


      Anschließend startet der Installer den Installationsvorgang. Hier heißt es dann: Warten und Tee trinken.


      Nach der Installation siehst Du dann den Control Panel von XAMPP. Wir benötigen hier nur die ersten beiden Teile, den Apache und MySQL.


      Klicke nun sowohl bei Apache als auch bei MySQL auf den "Start"-Button. Wenn beide Services korrekt gestartet worden sind, dann siehst Du folgendes:


      Sollte hier ein Fehler angezeigt werden, dann ist dies oftmals durch Skype verschuldet, da Skype den gleichen Port wie Apache nutzt.
      Klappe, falls Du Probleme mit dem Port hast, folgenden Spoiler auf:




    • Anlegen einer Datenbank in phpMyAdmin
      Um nun eine Datenbank anzulegen, in der die Daten gespeichert werden, müssen wir mit phpMyAdmin arbeiten. Klicke hierzu auf den "Admin"-Button von MySQL.


      Es öffnet sich nun in Deinem Standard-Browser die phpMyAdmin Startseite.


      Um jetzt eine neue Datenbank anzulegen, klicke bitte auf "Neu" am linken Randmenü.


      Gebe dort nun den gewünschten Datenbankname ein. Ich nehme für das Beispiel "samp_db". Wenn Du problemlos durch das Tutorial gehen möchtest ist es zu empfehlen, dass Du auch diesen Namen verwendest. Danach drücke bitte auf "Anlegen".


      Die Datenbank wurde nun angelegt. Um sie zu öffnen musst Du die Datenbank am linken Randmenü auswählen.


      Da in der neu erstellten Datenbank noch keine Tabelle vorhanden ist gibt uns phpMyAdmin vor eine neue Tabelle anzulegen. Wir legen uns eine Tabelle wie im obigen Beispiel an. Diese Tabelle nennen wir "users" und sie soll 7 Spalten haben.


      Anschließend siehst Du die Maske um die Spalten der Tabelle zu pflegen.
      Gebe bitte diese Daten ein:

      Code
      id - INT (10) - Keine - Index: PRIMARY - A_I angehakt
      name - VARCHAR (64) - NULL
      password - VARCHAR (128) - NULL
      level - INT (3) - Wie definiert: 0
      money - INT (10) - Wie definiert: 0
      kills - INT (10) - Wie definiert: 0
      deaths - INT (10) - Wie definiert: 0



      Das sieht dann so aus:

      Klicke dann auf "Speichern" um die Tabelle anzulegen.


      Die fertig erstellte Tabelle "users" sieht dann in phpMyAdmin so aus:


      Nun ist die Tabelle bereit Daten aufzunehmen. Wie das geht werden wir im nächsten Kapitel behandeln.







    5.) SA-MP Server und MySQL Datenbank verbinden

    • Benötigte Dateien
      Jetzt sind wir an dem Punkt angekommen, an dem es interessant wird. Wir bauen die Verbindung zwischen dem SA-MP Server und der MySQL Datenbank auf. Hierzu benötigen wir einige Dateien. Diese kannst Du hier herunterladen: Download
      In diesem Ordner findest Du nun einige Dateien. Für die Verwendung auf einem Windows-System werden vier Dateien benötigt, diese sind: a_mysql.inc, mysql.dll, libmariadb.dll und log-core.dll. Füge nun bitte die a_mysql.inc Datei in das Verzeichnis Deines SA-MP Servers unter /pawno/includes/ ein. Die mysql.dll legst Du bitte im /plugins/ Ordner ab. Sollte sich dieser Ordner noch nicht in dem Hauptordner Deines SA-MP Servers befinden, erstelle diesen Ordner zuerst und füge die mysql.dll Datei dann dort ein. Die libmariadb.dll sowie die log-core.dll legst Du bitte in den Hauptordner Deines Servers.
      Als nächstes musst Du die server.cfg Datei öffnen, die sich im Hauptordner Deines SA-MP Servers befindet. Trage dort in der Zeile "plugins" das MySQL Plugin ein, indem Du mysql zu der Zeile hinzufügst. Sollte in der server.cfg Datei noch keine plugins-Zeile vorhanden sein, füge am Ende der Datei "plugins mysql" in einer neuen Zeile ein.


    • Verbindungsaufbau
      Um nun die Verbindung herzustellen benötigen wir einen PAWN Code. Öffne hierzu bitte die pawno.exe, die sich im Ordner /pawno/ befindet. Wähle dann entweder einen existierenden Code aus oder erstelle eine neue Datei. Es spielt herbei keine Rolle, ob der MySQL Code in einem Filterscript oder einem Gamemode aufgebaut wird, beides funktioniert ohne Probleme.
      Als erstes musst Du zu den Includes die MySQL-Include hinzufügen:

      C
      #include <a_mysql>



      Jetzt benötigen wir die Login Daten für die Datenbank. Diese hast Du ja zuvor bereits mit XAMPP und phpMyAdmin aufgesetzt. Die Login Daten sind hierfür standardmäßig der root Benutzer. Das kannst Du auch über phpMyAdmin ändern, indem Du einen neuen Benutzer anlegst, allerdings kommt es häufig zu Problemen mit den Berechtigungen, daher ist es zu empfehlen, dem root Benutzer später nur ein Passwort zu geben, das reicht, vor allem für Tests, völlig aus.
      Solltest Du die vorherigen Schritte übersprungen haben, da Du Deinen Server bei einem Hoster liegen hast, dann musst Du die Login Daten angeben, die in Deinem Control Panel des Servers angezeigt werden. Frage gegebenenfalls bei dem Support Deines Hosters nach, wenn Du nicht weißt, welche Daten das sind, beziehungsweise wo Du diese Daten findest.
      Wir geben nun also die Daten in den Code ein, dazu legen wir Konstanten an:

      C
      #define MYSQL_HOST    "127.0.0.1"      //IP Adresse des MySQL Servers
      #define MYSQL_USER    "root"           //Benutzername der angemeldet wird
      #define MYSQL_PASS    ""               //Passwort des Benutzers
      #define MYSQL_DBSE    "samp_db"        //Name der Datenbank



      Solltest Du andere Daten haben, nutze bitte Deine Daten. Hier nochmals der Hinweis, wenn Du zuvor das root-Passwort geändert hast, dieses Passwort zu verwenden.
      Außerdem benötigen wir noch eine Variable, die uns die Verbindungs-ID speichert. Füge direkt unter den Konstanten folgendes ein:

      C
      new MySQL:handle; //Die Connection-Handle, über die wir später auf die Tabellen der Datenbank zugreifen



      Um jetzt die Verbindung mit diesen Daten herzustellen erstellen wir uns eine kleine Funktion, die das für uns macht, vor allem auch deshalb, damit OnGameModeInit/OnFilterScriptInit nicht zu unübersichtlich wird, da dort oft ziemlich viel drin steht. Dazu musst Du bei OnGameModeInit/OnFilterScriptInit folgenden Funktionsaufruf einfügen:

      C
      MySQL_SetupConnection();



      Es besteht die Möglichkeit, zwischen den Klammern eine Zahl einzufügen, die steht dann für die Anzahl der Verbindungsversuche (TTL = Time To Lift). Standardmäßig ist das 3, wenn Du mehr oder weniger willst, trage die entsprechende Zahl dort ein, ansonsten lasse es leer.


      Ganz unten in Deinem Gamemode/Filterscript fügst Du dann diese Funktion ein:



      Mit diesem Code haben wir uns nun mehrfach abgesichert, damit auch wirklich eine Datenbankverbindung aufgebaut wird.


      Um die Verbindung zu Beenden, wenn der Gamemode/Filterscript beendet wird, füge bei OnGameModeExit/OnFilterScriptExit diese Zeile ein:

      C
      mysql_close(handle);



      Kompiliere jetzt Deinen Code und starte zum Test Deinen Server. Im besten Fall siehst Du in der Console folgende Meldung:

      Ist dies der Fall, dann kannst Du mit dem nächsten Schritt fortfahren. Sollten Probleme auftauchen, schalte den Debug-Modus an und prüfe Deine Daten. Falls Du Hilfe benötigst kannst Du gerne in diesem Thread nach Hilfe fragen, oder einen neuen Thread in der Scripting Base erstellen.



    6.) Register & Login System

    • Coding
      Da wir jetzt die Verbindung aufgebaut haben, können wir mit dem eigentlich wichtigen Teil beginnen, nämlich dem Speichersystem für die Benutzer Deines Servers, schließlich sollen deren Daten ja gespeichert werden. Hierzu benötigen wir diverse Code-Teile, die ich im folgenden Abschnitt erkläre. Wir gehen chronologisch vom Betreten des Servers bis zum Verlassen des Servers vor, und beginnen bei der Deklaration der Variablen.


      Um die Daten zur Laufzeit temporär zu speichern, ohne jedes mal auf die Datenbank zugreifen zu müssen legen wir ein Array mit den Spielerdaten an. Für eine komfortablere Bedienung nutzen wir ein Enum. Füge hierzu unter den Includes - also ganz oben in Deinem Gamemode/Filterscript - diese Deklaration ein:



      Anschließend gehst Du zu OnPlayerConnect. Wenn der Spieler den Server betritt, dann soll er initialisiert werden, sprich die Variablen werden sicherheitshalter zurückgesetzt. Dazu benötigst Du diesen Code:



      Als nächstes kommt der Spieler in die Class-Selection. Hier wird OnPlayerRequestClass aufgerufen. Dort fragen wir in der Datenbank ab, ob der Spieler bereits registriert ist. Dies geht so:



      Außerdem benötigen wir nun das Callback OnUserCheck, welches Du am besten ganz unten in Deinem Gamemode/Filterscript einfügst.



      Damit der Code ohne Fehler kompiliert, musst Du unter den Includes die beiden Dialoge definieren:

      C
      //Dialog IDs (gegebenenfalls ändern, falls bereits belegt)
      #define DIALOG_REGISTER  1403
      #define DIALOG_LOGIN     2401



      Da dem Spieler nun Dialoge angezeigt werden benötigst Du das Callback OnDialogResponse. Dort trägst Du ein, was passiert, wenn der Spieler sich registrieren oder einloggen will, je nach dem welchen Dialog der Spieler angezeigt bekommen hat.


      Auch hier benötigst Du die beiden neuen Callbacks, die Du wieder ganz unten in Deinem Gamemode/Filterscript einfügst:

      C
      forward OnUserRegister(playerid);
      public OnUserRegister(playerid)
      {
      	//Der Spieler wurde in die Datenbank eingetragen, es wird die id ausgelesen
      	PlayerInfo[playerid][p_id] = cache_insert_id();
      	PlayerInfo[playerid][pLoggedIn]  = true;
      	SendClientMessage(playerid, 0x00FF00FF, "[Konto] Registration erfolgreich.");
      	return 1;
      }




      So, jetzt hast Du es fast geschafft. Es fehlt nur noch die Speicherung der Spielerdaten, wenn der Spieler den Server wieder verlässt.

      C
      public OnPlayerDisconnect(playerid, reason)
      {
      	//Speichere den Spieler wenn er der Server verlässt
      	SaveUserStats(playerid);
      	return 1;
      }


      Für die Speicherung nutzen wir dann diese Funktion:



      Damit wären wir so gut wie durch. Um die Funktionen auch testen zu können schreiben wir noch etwas bei OnPlayerDeath rein:



      Damit sind wir durch. Kompiliere Deinen Code und starte den Server neu.
      Der Spieler kann sich nun registrieren, einloggen und seine Statistiken werden beim Verlassen des Servers gespeichert. Die Statistiken kannst Du gegebenenfalls auch in einem Timer oder in einem Befehl speichern lassen, das bleibt Dir überlassen.


      Wenn der Spieler nun den Server betritt, dann wird ihm der Register-/Login-Dialog angezeigt.
      In der Datenbank sieht ein neuer Spieler nach der Registration so aus:


      Und nachdem er ein paar Dinge gemacht hat und den Server verlässt, sieht es so aus:

      Aktualisiere (F5), falls nötig, die Ansicht, damit die Daten korrekt angezeigt werden.



      Dieses Register-/Login-System kannst Du nun nach Belieben ausbauen, da es ja noch recht spartanisch ist. Aber Du weißt jetzt wie es geht und kannst Dich ohne Probleme durch die Materie arbeiten.





    7.) Hinweise und Fehlerbehebung

    • Der Debug Modus
      Mit dem MySQL Plugin kommt ein sehr nützlicher Debug Modus, mit dem Du alles was das Plugin macht überprüfen kannst, denn jeder Schritt wird in einem Log ausgegeben. Sollte es also einmal vorkommen, dass Du einen MySQL Code hast, der aber aus unbekannten Gründen nicht das macht, was er soll, dann kannst Du in dem MySQL Log nachschauen, was denn eigentlich passiert, oder ob möglicherweise Fehler ausgegeben werden.
      Um den MySQL Debug Modus einzuschalten musst Du nichts weiter machen, als bei OnGameModeInit/OnFilterScriptInit folgenden Funktionsaufruf hinzuzufügen:

      C
      mysql_log();


      Dies bewirkt, dass alle Aktionen des Plugins geloggt werden. Dieser Log findet sich als mysql_log.txt im Hauptverzeichnis des SA-MP Servers wieder, dort wo auch die server_log.txt liegt. Du musst Dir aber im Klaren darüber sein, dass dieser Debug Modus nicht die ganze Zeit auf dem produktiven System laufen sollte, sondern nur dann, wenn er auch benötigt wird, da durch das ganze Logging die Performance des Plugins eingeschränkt wird.
      Eine Liste zu allen Fehler-Ausgaben von MySQL findest Du hier.

    • CentOS: Failed ( libz.so.1 & libmysqlclient.so.18 )


    • Plugin wird nicht geladen
      In manchen Fällen kann es vorkommen, dass das Plugin nicht gestartet wird. Es gibt unterschiedliche Gründe dafür, diese sind häufig auf fehlende Installationen im Betriebssystem zurückzuführen. Folgende Lösungsansätze helfen in den meisten Fällen:

      • Windows: Download und Installation der All-In-One Runtimes (Download von chip.de)
      • Linux: Nutzung der mysql_static.so anstelle der mysql.so.



    8.) Fußnoten

    • * unbegrenzt
      MySQL Tabelle sind natürlich nur theoretisch unbegrenzt, alles andere wäre unrealistisch, allerdings ist es kaum möglich, dass Du die existierenden Limits erreichst. Jeder Eintrag in der Datenbank benötigt Speicher, das heißt je mehr Einträge Deine Datenbank hat, desto mehr Speicher wird benötigt. Somit sind die Grenzen vor allem durch das Betriebssystem beziehungsweise die Festplattengröße begrenzt. Genauere Informationen zu den Limits findest Du hier: http://dev.mysql.com/doc/refman/5.6/en/limits.html




    9.) Für die Faulen unter uns

    • Wie immer habe ich für die Faulen unter uns einen fertigen Filterscript. Im Download befindet sich zudem die Datenbank, falls jemand die importieren möchte.



    • Zusätzlich verlinke ich hier noch auf folgende Include von JustMe.77: [INCLUDE] Einzigartige Login/Register TextDraws
      In den dortigen Downloads findet sich ein fertiger Filterscript, der den Code dieses Tutorials beinhaltet.




    Änderungen:

    • 14.08.2015: Übersetzung korrigiert und Hinweis bei Debugging hinzugefügt, danke an maddin.
    • 06.09.2015: Hinweis zu CentOS Problem hinzugefügt, danke an Scene-Sector.
    • 08.11.2015: Code-Formatierung an WBB4 angepasst.
    • 19.03.2016: Spoiler-Formatierung repariert.
    • 14.09.2016: Aktualisierung des Tutorials auf R40 und Verweise auf die SQL-Syntaxliste, das Wiki sowie W3Schools in Abschnitt 3 hinzugefügt.
    • 03.10.2016: Aktualisierung des Tutorials auf R41.
    • 21.05.2017: Aktualisierung des Tutorials auf R41-2 und beheben eines Login Fehlers nach der Registration, vielen Dank an JustMe.77.
    • 02.09.2017: Aktualisierung des Tutorials auf R41-3. Verlinkung Login/Register Textdraw System, vielen Dank an JustMe.77.
    • 01.10.2017: Aktualisierung des Tutorials auf R41-4.




    Sollten während oder nach dem Tutorial Fragen oder Probleme auftauchen, dann kannst Du natürlich gerne in diesem Thread oder in der Scripting Base nachfragen.
    Viel Spaß damit! :)


    Beste Grüße,
    Jeffry 8)

    Dateien

    3HZXdYd.png

    9 Mal editiert, zuletzt von Jeffry () aus folgendem Grund: Edit 1: 14.08.2015 | Edit 2: 06.09.2015 | Edit 3: 08.11.2015 | Edit 4: 19.03.2016 | Edit 5: 14.09.2016 | Edit 6: 03.10.2016 | Edit 7: 21.05.2017 | Edit 8: 02.09.2017 | Edit 9: 01.10.2017

  • Bei mir kommt diese errors...
    Und die Dialoge werden nicht angezeigt


    [16:35:51] [DEBUG] mysql_connect - host: "127.0.0.1", user: "root", database: "samp_db", password: "****", port: 3306, autoreconnect: true, pool_size: 2
    [16:35:51] [DEBUG] CMySQLHandle::Create - creating new connection..
    [16:35:51] [DEBUG] CMySQLHandle::CMySQLHandle - constructor called
    [16:35:51] [DEBUG] CMySQLHandle::Create - connection created (id: 1)
    [16:35:51] [DEBUG] CMySQLConnection::Connect - establishing connection to database...
    [16:35:51] [DEBUG] CMySQLConnection::Connect - connection was successful
    [16:35:51] [DEBUG] CMySQLConnection::Connect - auto-reconnect has been enabled
    [16:35:51] [DEBUG] mysql_errno - connection: 1
    [16:35:51] [DEBUG] CMySQLConnection::Connect - establishing connection to database...
    [16:35:51] [DEBUG] CMySQLConnection::Connect - establishing connection to database...
    [16:35:51] [DEBUG] CMySQLConnection::Connect - establishing connection to database...
    [16:35:51] [DEBUG] CMySQLConnection::Connect - connection was successful
    [16:35:51] [DEBUG] CMySQLConnection::Connect - connection was successful
    [16:35:51] [DEBUG] CMySQLConnection::Connect - connection was successful
    [16:35:51] [DEBUG] CMySQLConnection::Connect - auto-reconnect has been enabled
    [16:35:51] [DEBUG] CMySQLConnection::Connect - auto-reconnect has been enabled
    [16:35:51] [DEBUG] CMySQLConnection::Connect - auto-reconnect has been enabled

  • Bei mir kommt diese errors...


    Das sind keine Errors, das sind nur Debug-Meldungen, dass die Verbindung erfolgreich hergestellt wurde. Von daher ist das super so :)


    Hast du die Dialoge mal auf einem leeren Server (z.B. grandlarc) getestet? Möglicherweise wird das Callback nicht aufgerufen, weil es von einem anderen Code blockiert wird.

  • Gutes Tutorial.


    Ich habe die Variante von Maddin in mein Script übernommen (stock mysql_GetInt, stock mysql_GetString usw.). :D


    Eine Frage:
    Warum ist Maddins Variante (Tutorial) "veraltet", beziehungsweise, ist diese Variante noch zu empfehlen, das mit dem Funtkionen erstellen, um die Spielerdaten zu holen?
    (Merke selber schon, dass mit Maddins Variante, der Login etwas länger dauert um die Spielerdaten zu laden, weiß nicht ob es an den Funktionen oder am MySQL Plugin (R5) liegt)

  • Danke!


    Warum ist Maddins Variante (Tutorial) "veraltet", beziehungsweise, ist diese Variante noch zu empfehlen, das mit dem Funtkionen erstellen, um die Spielerdaten zu holen?


    Das R5 Plugin kann nicht mit mehreren Threads und Caches umgehen, das ist der Hauptgrund für den Geschwindigkeitsunterschied.


    Diese Funktionen für einfache Aufrufe sollten gemieden werden, denn wenn du 50 Stats lädst, dann werden 50 SELECT Queries gesendet, und das dauert. Das ist zwar eine einfache Variante, aber so ziemlich die schlechteste von der Performance her.

  • Hey,
    mir ist ein kleiner Fehler aufgefallen undzwar beim Speichern, speicherst die Tode als deaths aber die Spalte heißt death.
    Du hast überall diesen Fehler gemacht


    Fail, Bin wohl Müde. :dash::sleeping: :sleeping: //Edit 2: Abgesehen davon, super Tutorial!

    Mit freundlichen Grüßen,
    Chriss aka. xDesertx

    Einmal editiert, zuletzt von xDesertx ()

  • Failed (libmysqlclient.so.18: cannot open shared object file: No such file or directory)


    :D Klappt auf CentOs aber ich brauche die Libs :D


    Hab es mal so gemacht :) Da diese noch im 64 Bit Verzeichniss waren und habe diese auch ins 32 Bit kopiert!


    "ln -s /usr/lib64/libperconaserverclient.so.18 /usr/lib/libmysqlclient.so.18"

  • Failed (libz.so.1: cannot open shared object file: No such file or directory)


    Nächster Fehler ... Ohmann !


    Fixxed :


  • Ich habe dir den kompletten Fix und die Lösung für andere per PM zukommen lassen, einfach dein Tutorial aktualisieren ! :D


    Hier eine Problemlösung für die OS Version : CentOS 7.1 , 6.X



    Dann folgendes :


    stock MySQL_SetupConnection(ttl = 3)
    {
    print("[MySQL] Verbindungsaufbau...");
    mysql_log(LOG_ALL); //<- Kommentar vor mysql_log entfernen um den MySQL Debug-Modus zu aktivieren

    //Prüfen und gegebenenfalls wiederholen
    if(mysql_errno(handle) != 0)
    {
    //Fehler im Verbindungsaufbau, prüfe ob ein weiterer Versuch gestartet werden soll
    if(ttl > 1)
    {
    //Versuche erneut eine Verbindung aufzubauen
    print("[MySQL] Es konnte keine Verbindung zur Datenbank hergestellt werden.");
    printf("[MySQL] Starte neuen Verbindungsversuch (TTL: %d).", ttl-1);
    return MySQL_SetupConnection(ttl-1);
    }
    else
    {
    //Abbrechen und Server schließen
    print("[MySQL] Es konnte keine Verbindung zur Datenbank hergestellt werden.");
    print("[MySQL] Bitte prüfen Sie die Verbindungsdaten.");
    print("[MySQL] Der Server wird heruntergefahren.");
    return SendRconCommand("exit");
    }
    }
    printf("[MySQL] Die Verbindung zur Datenbank wurde erfolgreich hergestellt! Handle: %d", handle);
    return 1;
    }


    handle = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_DBSE, MYSQL_PASS);


    Der handler für den Datenbankaufbau kommt unter OnGameModeInit und nicht in den Stock ! Denn sonst passiert das hier :


    Zitat

    [02:26:40] [ERROR] mysql_pquery - invalid connection handle (id: 0)
    [02:28:41] [ERROR] mysql_close - invalid connection handle (id: 0)

    Einmal editiert, zuletzt von Scene-Sector () aus folgendem Grund: Bugfixxes !

  • Jeffry:


    SA-MP Dedicated Server
    ----------------------
    v0.3.7-R2, (C)2005-2015 SA-MP Team

    [03:07:42]
    [03:07:42] Server Plugins
    [03:07:42] --------------
    [03:07:42] Loading plugin: mysql
    [03:07:42] >> plugin.mysql: R39-3 successfully loaded.
    [03:07:42] Loaded.
    [03:07:42] Loaded 1 plugins.


    [03:07:42]
    [03:07:42] Filterscripts
    [03:07:42] ---------------
    [03:07:42] Loaded 0 filterscripts.


    [03:07:42] MySQL: Die Verbindung wurde erfolgreich zur Datenbank hergestellt.
    [03:07:42] MySQL: Die Verbindung wurde erfolgreich zur Datenbank hergestellt.
    [03:07:42] Number of vehicle models: 0


    Warum kommt das bei mir 2x ?

  • Jeffry ich habe es so gemacht wie du es auch gemacht hast und es sieht bei mir so aus:


    Code:
    #include <a_samp>
    #include<a_mysql>
    //defines
    #define MYSQL_HOST "127.0.0.1"
    #define MYSQL_USER "root"
    #define MYSQL_PASS ""
    #define MYSQL_DBSE "sampdeathmatch"
    new handle;
    forward OnUserCheck(playerid);
    forward OnUserRegister(playerid);
    forward OnUserLogin(playerid);
    enum pDataEnum
    {
    p_id,
    bool:pLoggedIn,
    pName[MAX_PLAYER_NAME],
    pLevel,
    pMoney,
    pKills,
    pTode
    }
    new PlayerInfo[MAX_PLAYERS][pDataEnum];

    //Dialoge:
    #define DIALOG_REGISTER 1
    #define DIALOG_LOGIN 2
    main()
    {
    print("\n----------------------------------");
    print(" Blackshadow Deathmatch");
    print("----------------------------------\n");
    }

    public OnGameModeInit()
    {
    MySQL_SetupConnection();
    SetGameModeText("Blackshadow Deathmatch");
    AddPlayerClass(0, 1958.3783, 1343.1572, 15.3746, 269.1425, 0, 0, 0, 0, 0, 0);
    return 1;
    }


    public OnGameModeExit()
    {
    mysql_close(handle);
    return 1;
    }


    public OnPlayerRequestClass(playerid, classid)
    {
    SetPlayerPos(playerid, 1958.3783, 1343.1572, 15.3746);
    SetPlayerCameraPos(playerid, 1958.3783, 1343.1572, 15.3746);
    SetPlayerCameraLookAt(playerid, 1958.3783, 1343.1572, 15.3746);

    //Wenn der Spieler die Class-Selection betritt prüfe, ob er bereits eingeloggt ist
    if(!PlayerInfo[playerid][pLoggedIn])
    {
    //Wenn nicht, dann prüfe ob der Spieler ein Konto hat
    //Dazu wird ein Query gesendet und ein neues Callback aufgerufen
    //%e steht für einen geprüften String (sollte anstatt %s in Queries verwendet werden)
    new query[128];
    mysql_format(handle, query, sizeof(query), "SELECT id FROM users WHERE name = '%e'", PlayerInfo[playerid][pName]);
    mysql_pquery(handle, query, "OnUserCheck", "d", playerid);
    }
    return 1;
    }


    public OnPlayerConnect(playerid)
    {
    PlayerInfo[playerid][p_id] = 0;
    PlayerInfo[playerid][pLoggedIn] = false;
    PlayerInfo[playerid][pLevel] = 0;
    PlayerInfo[playerid][pMoney] = 0;
    PlayerInfo[playerid][pKills] = 0;
    PlayerInfo[playerid][pTode] = 0;
    GetPlayerName(playerid,PlayerInfo[playerid][pName],MAX_PLAYER_NAME);
    return 1;
    }


    public OnPlayerDisconnect(playerid, reason)
    {
    SaveUserStats(playerid);
    return 1;
    }


    public OnPlayerSpawn(playerid)
    {
    return 1;
    }


    public OnPlayerDeath(playerid, killerid, reason)
    {
    //Beispielcode
    if(killerid != INVALID_PLAYER_ID)
    {
    PlayerInfo[killerid][pKills]++;
    GivePlayerMoney(killerid, 10);
    PlayerInfo[killerid][pMoney] += 10;
    if(PlayerInfo[killerid][pKills] > 3)
    {
    PlayerInfo[killerid][pLevel] = 1;
    }
    }
    PlayerInfo[playerid][pDeaths]++;
    return 1;
    }


    public OnVehicleSpawn(vehicleid)
    {
    return 1;
    }


    public OnVehicleDeath(vehicleid, killerid)
    {
    return 1;
    }


    public OnPlayerText(playerid, text[])
    {
    return 1;
    }


    public OnPlayerCommandText(playerid, cmdtext[])
    {
    if (strcmp("/mycommand", cmdtext, true, 10) == 0)
    {
    // Do something here
    return 1;
    }
    return 0;
    }


    public OnPlayerEnterVehicle(playerid, vehicleid, ispassenger)
    {
    return 1;
    }


    public OnPlayerExitVehicle(playerid, vehicleid)
    {
    return 1;
    }
    public OnPlayerStateChange(playerid, newstate, oldstate)
    {
    return 1;
    }


    public OnPlayerEnterCheckpoint(playerid)
    {
    return 1;
    }


    public OnPlayerLeaveCheckpoint(playerid)
    {
    return 1;
    }


    public OnPlayerEnterRaceCheckpoint(playerid)
    {
    return 1;
    }


    public OnPlayerLeaveRaceCheckpoint(playerid)
    {
    return 1;
    }


    public OnRconCommand(cmd[])
    {
    return 1;
    }


    public OnPlayerRequestSpawn(playerid)
    {
    return 1;
    }


    public OnObjectMoved(objectid)
    {
    return 1;
    }


    public OnPlayerObjectMoved(playerid, objectid)
    {
    return 1;
    }


    public OnPlayerPickUpPickup(playerid, pickupid)
    {
    return 1;
    }


    public OnVehicleMod(playerid, vehicleid, componentid)
    {
    return 1;
    }


    public OnVehiclePaintjob(playerid, vehicleid, paintjobid)
    {
    return 1;
    }


    public OnVehicleRespray(playerid, vehicleid, color1, color2)
    {
    return 1;
    }


    public OnPlayerSelectedMenuRow(playerid, row)
    {
    return 1;
    }


    public OnPlayerExitedMenu(playerid)
    {
    return 1;
    }


    public OnPlayerInteriorChange(playerid, newinteriorid, oldinteriorid)
    {
    return 1;
    }


    public OnPlayerKeyStateChange(playerid, newkeys, oldkeys)
    {
    return 1;
    }


    public OnRconLoginAttempt(ip[], password[], success)
    {
    return 1;
    }


    public OnPlayerUpdate(playerid)
    {
    return 1;
    }


    public OnPlayerStreamIn(playerid, forplayerid)
    {
    return 1;
    }


    public OnPlayerStreamOut(playerid, forplayerid)
    {
    return 1;
    }


    public OnVehicleStreamIn(vehicleid, forplayerid)
    {
    return 1;
    }


    public OnVehicleStreamOut(vehicleid, forplayerid)
    {
    return 1;
    }
    public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
    {
    if(dialogid == DIALOG_REGISTER)
    {
    //Spieler hat Abbrechen gewählt
    if(!response) return Kick(playerid);

    //Wenn der Spieler kein, oder ein zu kurzes, Passwort eingegeben hat
    if(strlen(inputtext) < 3) return ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration", "Bitte registriere Dich:\n{FF0000}Mindestens 3 Zeichen!", "Ok", "Abbrechen");

    //Wenn alles passt wird der Spieler in der Datenbank angelegt
    //Als Verschlüsselung für das Passwort wird MD5 verwendet
    new query[256];
    mysql_format(handle, query, sizeof(query), "INSERT INTO users (name, password) VALUES ('%e', MD5('%e'))", PlayerInfo[playerid][pName], inputtext);

    //Das Query wird abgesendet und die playerid an OnUserRegister übergeben
    mysql_pquery(handle, query, "OnUserRegister", "d", playerid);
    return 1;
    }
    if(dialogid == DIALOG_LOGIN)
    {
    //Spieler hat Abbrechen gewählt
    if(!response) return Kick(playerid);


    //Wenn der Spieler kein, oder ein zu kurzes, Passwort eingegeben hat
    if(strlen(inputtext) < 3) return ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Anmeldung", "Bitte logge Dich ein:\n{FF0000}Mindestens 3 Zeichen!", "Ok", "Abbrechen");


    //Wenn alles passt wird die Datenbank ausgelesen
    new query[256];
    mysql_format(handle, query, sizeof(query), "SELECT * FROM users WHERE name = '%e' AND password = MD5('%e')", PlayerInfo[playerid][pName], inputtext);


    //Das Query wird abgesendet und die playerid an OnUserLogin übergeben
    mysql_pquery(handle, query, "OnUserLogin", "d", playerid);
    return 1;
    }
    return 0;
    }


    public OnPlayerClickPlayer(playerid, clickedplayerid, source)
    {
    return 1;
    }


    stock MySQL_SetupConnection(ttl = 3)
    {
    print("[MySQL] Verbindungsaufbau...");
    //mysql_log(LOG_ALL); //<- Kommentar vor mysql_log entfernen um den MySQL Debug-Modus zu aktivieren

    handle = mysql_connect(MYSQL_HOST, MYSQL_USER, MYSQL_DBSE, MYSQL_PASS);

    //Prüfen und gegebenenfalls wiederholen
    if(mysql_errno(handle) != 0)
    {
    //Fehler im Verbindungsaufbau, prüfe ob ein weiterer Versuch gestartet werden soll
    if(ttl > 1)
    {
    //Versuche erneut eine Verbindung aufzubauen
    print("[MySQL] Es konnte keine Verbindung zur Datenbank hergestellt werden.");
    printf("[MySQL] Starte neuen Verbindungsversuch (TTL: %d).", ttl-1);
    return MySQL_SetupConnection(ttl-1);
    }
    else
    {
    //Abbrechen und Server schließen
    print("[MySQL] Es konnte keine Verbindung zur Datenbank hergestellt werden.");
    print("[MySQL] Bitte prüfen Sie die Verbindungsdaten.");
    print("[MySQL] Der Server wird heruntergefahren.");
    return SendRconCommand("exit");
    }
    }
    printf("[MySQL] Die Verbindung zur Datenbank wurde erfolgreich hergestellt! Handle: %d", handle);
    return 1;
    }
    public OnUserCheck(playerid)
    {
    //Query wurde ausgeführt und das Ergebnis im Cache gespeichert
    if(cache_get_row_count() == 0)
    {
    //Der Spieler konnte nicht gefunden werden, er muss sich registrieren
    ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registration", "Bitte registriere Dich:", "Ok", "Abbrechen");
    }
    else
    {
    //Es existiert ein Ergebnis, das heißt der Spieler ist registriert und muss sich einloggen
    ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Anmeldung", "Bitte logge Dich ein:", "Ok", "Abbrechen");
    }
    return 1;
    }
    public OnUserRegister(playerid)
    {
    //Der Spieler wurde in die Datenbank eingetragen, es wird die id ausgelesen
    PlayerInfo[playerid][p_id] = cache_insert_id();
    SendClientMessage(playerid, 0x00FF00FF, "[Konto] Registration erfolgreich.");
    return 1;
    }


    public OnUserLogin(playerid)
    {
    //Query wurde ausgeführt und das Ergebnis im Cache gespeichert
    if(cache_get_row_count() == 0)
    {
    //Der Spieler hat ein falsches Passwort eingegeben
    ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Anmeldung", "Bitte logge Dich ein:\n{FF0000}Falsches Passwort!", "Ok", "Abbrechen");
    }
    else
    {
    //Es existiert ein Ergebnis, das heißt der Spieler hat das richtige Passwort eingegeben
    //Wir lesen nun die erste Zeile des Caches aus (ID 0)
    PlayerInfo[playerid][p_id] = cache_get_field_content_int(0, "id", handle);
    PlayerInfo[playerid][pLevel] = cache_get_field_content_int(0, "level", handle);
    PlayerInfo[playerid][pMoney] = cache_get_field_content_int(0, "money", handle);
    PlayerInfo[playerid][pKills] = cache_get_field_content_int(0, "kills", handle);
    PlayerInfo[playerid][pDeaths] = cache_get_field_content_int(0, "deaths", handle);
    PlayerInfo[playerid][pLoggedIn] = true;
    SendClientMessage(playerid, 0x00FF00FF, "[Konto] Eingeloggt.");
    GivePlayerMoney(playerid, PlayerInfo[playerid][pMoney]);
    }
    return 1;
    }
    stock SaveUserStats(playerid)
    {
    //Wenn der Spieler nicht eingeloggt ist, dann speichere seine Statistiken nicht
    if(!PlayerInfo[playerid][pLoggedIn]) return 1;

    //Ansonsten speichere sie
    new query[256];
    mysql_format(handle, query, sizeof(query), "UPDATE users SET level = '%d', money = '%d', kills = '%d', deaths = '%d' WHERE id = '%d'",
    PlayerInfo[playerid][pLevel], PlayerInfo[playerid][pMoney], PlayerInfo[playerid][pKills], PlayerInfo[playerid][pDeaths], PlayerInfo[playerid][p_id]);

    //Das Query wird abgesendet
    mysql_pquery(handle, query);
    return 1;
    }


    Wenn ich Compailen will kommt folgender fehler:


    Ungültige Zeile: "1103"
    Ungültige Zeile: "1367"
    Ungültige Zeile: "1381"


    Ich habe auch kein plan warum

  • Hallo
    Ich bin mit SA-MP noch nicht so vertaut. Wobei, weniger SA-MP als diese MySQL-Verbindung mein Problem ist. Aber ich denke hier bin ich richtig und Ihr könnt mir helfen.
    Das lesen der Daten aus meiner MySQL Datenbank funktioniert bereits. Aber ich brauche jetzt eine Funktion, mit der ich die Einträge in meiner Datenbank zählen kann.
    folgendes hatte ich mir gedacht:


    new query[128];
    mysql_format(dbhandle, query, sizeof(query), "SELECT COUNT(*) AS play FROM user WHERE allowed = '1' OR allowed = '0'");
    new zaehler = cache_get_field_content_int(0, "play", dbhandle);


    Das Ergebnis des SQL-Befehls gibt diese Tabelle raus, aber wie bekomme ich das Ergebnis in die Variable 'zaehler'?
    --------
    | play|
    ---------
    | 123|
    ---------