Beiträge von maddinat0r

    Woher soll ich das wissen, du hast ja keinen Code gepostet :)
    Ich würde zur Sicherheit die sampGDK nicht mit einbinden wenn du sie nicht verwendest.
    Ansonsten hast du anscheinend die amxplugin.cpp vergessen mitzucompilen.

    Aber es ist auch komplexer,


    Wasn so komplex dran, eine Query abzuschicken, welche dann ein public aufruft?


    Ich bleib immernoch beim R5, noch keine probleme gehabt und es ist auch einfach zu verstehen.


    Hnnngh
    Manchmal glaub ich, ich mach mir umsonst Mühe das Plugin zu entwickeln.



    heyhooo
    BlueG's MySQL Plugin. Warum?

    • regelmäßig Updates
    • guter Support (nicht nur weil es weit verbreitet ist und die meisten User es kennen, sondern auch weil der derzeitige Entwickler gerne per PN hilft)
    • R33+ hat auch unthreaded Queries (die, die es nur in R5/R6 gibt, die in R7 entfernt worden sind)
    • ORM-System
    • diverse andere Features (visuelles (per HTML) Logging, Cache-Zwischenspeicherung, mysql_log)


    Diese Funktion sucht einfach nach dem Zeichen ''' und ersetzt es durch "''" (also 2x ').

    Dein erstes Problem:
    stock SaveTanke()
    {
    for(new t; t<MAX_TANKE; t++) {
    new query[500/* + MAX_PLAYER_NAME*/]; //die 24 Zeichen kannst du dir auch sparen
    mysql_format(mycon, query, sizeof(query), "UPDATE `Tankstellen` SET `Name` = '%e', `Owned` = %d, `oName` = '%e', `PosX` = '%f', `PosY` = '%f', `PosZ` = '%f', `Preis` = '%d', `Spritpreis` = '%d' `Tankgeld` = '%d' WHERE `TankID` = '%i'",
    TankInfo[t][tName],
    TankInfo[t][Owned],
    TankInfo[t][oName],
    TankInfo[t][tX], TankInfo[t][tY], TankInfo[t][tZ],
    TankInfo[t][Preis], TankInfo[t][sPreis],
    TankInfo[t][Tankgeld],
    t //du kannst hier nicht einfach "t" hinklatschen, jede Zeile in der Tabelle hat
    //ihren eigenen Index. Du musst vorher in "LoadTanke" das "ID"-Feld aus der
    //Tabelle in eine Variable, wie z.B. "TankInfo[t][ID]" speichern und diese hier
    //anstelle von diesem "t" benutzen
    );
    mysql_tquery(mycon, query, "", "");
    }
    return 1;
    }


    Dein zweites Problem:
    cache_num_rows gibt es erst seit R33. Entweder du upgradest auf R33 oder du erstetzt new Rows = cache_num_rows(mycon); mit
    new Rows, Fields;
    cache_get_data(Rows, Fields, mycon);

    Warum benutzen Leute immer noch R7 wenn es schon R33 gibt? Völlig unverständlich.
    Wie auch immer, R33 für Linux MySQL Server 5.1 ist auf Debian 6.0 compiled, 5.5 ist auf Ubuntu 12.04 LTS compiled worden. Wenn du Debian 7.0 benutzt, wirst du warscheinlich MySQL 5.5 haben und somit die jeweilige Version benutzen müssen. Probier das erstmal aus.

    Bei deinem format in der 4. Zeile, es gibt kein %e im format, dass muss %i oder %d heißen ^^


    mfg. :thumbup:


    Er benutzt mysql_format, dort gibt es %e.



    [...]
    ich habe mysql_log(1); in OnGameModeInit aber es ist nirgens ein logfile


    :cursing::cursing::cursing:
    mysql_log != mysql_debug!!!!!
    Benutze entweder

    Code
    mysql_log(); //loggt Warnungen und Errors

    oder

    Code
    mysql_log(LOG_ERROR | LOG_WARNING | LOG_DEBUG); //loggt Warnungen, Errors und Debug-Nachrichten



    Hier ist der korrekte Code, die Kommentare sollten dir etwas helfen es zu verstehen:

    public OnGameModeInit()
    {
    //...
    mysql_tquery(mycon, "SELECT * FROM `Tankstellen`", "LoadTanke", ""); //sendet die Query, diese wählt alle Daten aus der Tabelle und ruft dann "LoadTanke" mit dem Ergebnis auf
    //...
    return 1;
    }


    forward LoadTanke();
    public LoadTanke() {
    new Rows = cache_num_rows(mycon); //Anzahl der Tankstellen bzw. Zeilen in der Tabelle
    for(new t; t < Rows; t++) { //hier gehen wir durch jede Zeile durch
    cache_get_field_content(t,"Name", TankInfo[t][tName], mycon, 64);
    cache_get_field_content(t,"oName", TankInfo[t][oName], mycon, 64);
    TankInfo[t][tX] = cache_get_field_content_float(t, "PosX");
    TankInfo[t][tY] = cache_get_field_content_float(t, "PosY");
    TankInfo[t][tZ] = cache_get_field_content_float(t, "PosZ");
    TankInfo[t][Preis] = cache_get_field_content_int(t, "Preis");
    TankInfo[t][sPreis] = cache_get_field_content_int(t, "Spritpreis");
    TankInfo[t][Tankgeld] = cache_get_field_content_int(t, "Tankgeld");
    /*switch(cache_get_field_content_int(t, "Owned")){
    case 0: TankInfo[t][Owned] = false;
    case 1: TankInfo[t][Owned] = true;
    }*/
    //das kann man wie folgt schreiben:
    TankInfo[t][Owned] = (cache_get_field_content_int(t, "Owned") == 1); //wenn "Owned" (aus der Tabelle) gleich 1, dann TankInfo[t][Owned] == true, ansonsten false
    if(TankInfo[t][Owned] == false) {
    new string[128];
    format(string,sizeof(string),"[Tankstellen ID: %d]\nName: %s\nPreis: %d\nTankstelle zu verkaufen!\nBenutze /tanke",t,TankInfo[t][tName],TankInfo[t][Preis]);
    TankLabel[t] = Create3DTextLabel(string,Türkis,TankInfo[t][tX],TankInfo[t][tY],TankInfo[t][tZ],10,0);
    AddStaticPickup(1272,2,TankInfo[t][tX],TankInfo[t][tY],TankInfo[t][tZ],0);
    }
    else {
    new string[128];
    format(string,sizeof(string),"[Tankstellen ID: %d]\nName: %s\nBesitzer: %s\nSpritpreis: %d\nBenutze /tanken",t,TankInfo[t][tName],TankInfo[t][oName],TankInfo[t][sPreis]);
    TankLabel[t] = Create3DTextLabel(string,Rot,TankInfo[t][tX],TankInfo[t][tY],TankInfo[t][tZ],10,0);
    AddStaticPickup(1272,2,TankInfo[t][tX],TankInfo[t][tY],TankInfo[t][tZ],0);
    }
    }
    return 1;
    }

    Das ist ein Tutorial für das ORM Feature des MySQL Plugins ab R33+. ORM steht für objekt-relationales Mapping und was das im Zusammenhang mit dem MySQL Plugin und PAWN zu tun hat, versuche ich in diesem Tutorial zu erklären.


    Dieses Feature, entwickelt von Pain123/Maddinat0r, ist dazu da, das Scripting für Leute zu vereinfachen, die einfach nur tolle Sachen scripten wollen anstatt sich um die SQL-Syntax und andere MySQL-spezifische Sachen sorgen zu machen. Das heißt, dass das ganze MySQL-Zeugs (von Query erstellen bis zur Ergebnisverarbeitung) vom Plugin übernommen wird.


    Das Erste das getan werden muss ist die Erstellung einer ORM-Instanz. Dies geschieht mit id = orm_create(tabellenname[]), wobei mit "tabellenname" bestimmt wird, welche Tabelle das ORM-System, oder besser gesagt diese ORM-Instanz, verwalten soll. Jetzt müssen ein paar globale Variablen an diese ORM-Instanz gebunden werden. Dies passiert mit Hilfe von diesen Funktionen:

    //globale Variablen
    new kills, Float:kd_ratio, name[MAX_PLAYER_NAME+1];


    // ... irgendwo in einer Funktion
    new ORM:id = orm_create("players");
    orm_addvar_int(id, kills, "kills"); // orm_addvar_int(ORM:id, variable, field_name[])
    orm_addvar_float(id, kd_ratio, "ratio"); // orm_addvar_float(ORM:id, Float:variable, field_name[])
    orm_addvar_string(id, name, sizeof(name), "name"); // orm_addvar_string(ORM:id, array[], max_len, field_name[])

    Diese Code-Zeilen fügen 3 Einträge hinzu, welche die zuvor erstellte ORM-Instanz im Auge behalten wird. Die Tabelle die in orm_create eingetragen wurde muss die Feld-Namen enthalten, die zuvor mit orm_addvar_* bestimmt wurden. Und - wie schon zuvor erwähnt - die Variable muss global deklariert worden sein - in anderen Worten sie darf nicht auf dem Stack sein.


    Um noch es noch einmal zusammenzufassen: dieses ORM-System führt für dich Queries aus und verarbeitet anschließend das Ergebnis (weist die Daten des Ergebnisses den Variablen zu).
    Hier ein realistisches Szenario, wo so ein System zum Einsatz kommen könnte. Der Sinn des folgenden Scripts ist ein grundlegendes User-System zu erstellen.

    #include <a_mysql>


    enum E_PLAYER {
    ORM:ORM_ID,
    ID,
    Name[MAX_PLAYER_NAME+1],
    Money,
    Level,
    Float:PosX,
    Float:PosY,
    Float:PosZ,
    };
    new Player[MAX_PLAYERS][E_PLAYER];


    public OnPlayerConnect(playerid)
    {
    GetPlayerName(playerid, Player[playerid][Name], MAX_PLAYER_NAME+1);
    new ORM:ormid = Player[playerid][ORM_ID] = orm_create("players");


    orm_addvar_int(ormid, Player[playerid][ID], "ID");
    orm_addvar_string(ormid, Player[playerid][Name], MAX_PLAYER_NAME+1, "Name");
    orm_addvar_int(ormid, Player[playerid][Money], "Money");
    orm_addvar_int(ormid, Player[playerid][Level], "Level");
    orm_addvar_float(ormid, Player[playerid][PosX], "PosX");
    orm_addvar_float(ormid, Player[playerid][PosY], "PosY");
    orm_addvar_float(ormid, Player[playerid][PosZ], "PosZ");

    orm_setkey(ormid, "Name");
    orm_select(ormid, "OnPlayerDataLoad", "d", playerid);
    return 1;
    }


    forward OnPlayerDataLoad(playerid);
    public OnPlayerDataLoad(playerid)
    {
    switch(orm_errno(Player[playerid][ORM_ID]))
    {
    case ERROR_OK: {
    ShowPlayerDialog(playerid, DIALOG_LOGIN, DIALOG_STYLE_PASSWORD, "Login", "Bitte gib dein Passwort ein.", "Login", "Abbruch");
    }
    case ERROR_NO_DATA: {
    ShowPlayerDialog(playerid, DIALOG_REGISTER, DIALOG_STYLE_PASSWORD, "Registrierung", "Bitte gib dein Passwort ein.", "Registrieren", "Abbruch");
    }
    }
    orm_setkey(Player[playerid][ORM_ID], "ID"); // setzt einen neuen Schlüssel für WHERE `ID` = ... in zukünftigen Queries!
    return 1;
    }


    public OnPlayerDisconnect(playerid, reason)
    {
    if(Player[playerid][ID] != 0)
    orm_update(Player[playerid][ORM_ID]);

    orm_destroy(Player[playerid][ORM_ID]);


    for(new E_PLAYER:e; e < E_PLAYER; ++e)
    Player[playerid][e] = 0;
    return 1;
    }

    Das Erstellen einer ORM-Instanz und das Hinzufügen der entsprechenden Variablen sollte hier nun nichts neues darstellen. Doch danach kommt orm_setkey, welches benötigt wird um die WHERE-Bedinung für deine Queries zu generieren.
    Dieser Teil von OnPlayerConnect

    orm_setkey(ormid, "Name");
    orm_select(ormid, "OnPlayerDataLoad", "d", playerid);

    macht folgendes:
    1. setzt das interne Feld, welches für den WHERE-Teil der Query benötigt wird, zu "Name"
    2. generiert diese Query: "SELECT `ID`,`Name`,`Money`,`Level`,`PosX`,`PosY`,`PosZ` FROM `players` WHERE `Name`='%s' LIMIT 1" (%s ist zu ersetzen mit dem Spieler-Namen)
    3. führt diese Query aus (nicht im PAWN-Thread)
    4. nimmt das Ergebnis der Query (die aktuelle Cache) und weist die Daten den entsprechend zuvor registrierten Variablen zu
    5. ruft OnPlayerDataLoad mit playerid as Parameter auf


    Abhängig davon was die Query findet, setzt das ORM-System einen Wert für die Error-Variable. Im Callback, welches von orm_select aufgerufen wird, muss überprüft werden, ob alles in Ordnung ist oder es einen Fehler gab. Der Error-Wert kann mit orm_errno abgefragt werden. Zur Zeit gibt es folgende mögliche Fehler bzw. Error-Werte:
    * ERROR_OK (0)
    * ERROR_NO_DATA (1)


    ERROR_OK, in unserem Beispiel, zeigt, dass es den Spieler-Account gibt und wir weiter mit unserem Login-Fenster fortfahren können. Die Spieler-Variablen (Player[playerid][...]) haben in diesem Moment nun alle richtigen Werte aus der Tabelle zugewiesen bekommen.


    Eine andere Sache hier ist, dass für zukünftige, effizientere Queries ein neues Schlüssel-Feld gesetzt wird:
    orm_setkey(Player[playerid][ORM_ID], "ID");
    Das bedeuted, dass jede zukünftige Query, die von orm_update (in OnPlayerDisconnect zu sehen), orm_select oder orm_delete generiert wird, am Ende wie folgt aussehen wird: "... WHERE `ID`='%d'" (%d ist zu erstzen mit Player[playerid][ID]). Wenn der Spieler disconnected, wird die ORM-Instanz mit orm_destroy(ORM:id) zerstört. Davor können alle ORM-Funktionen außer orm_select und orm_update frei verwendet werden.


    Sollte sich der Spieler registrieren müssen, wird orm_insert verwendet. Diese Funktion schickt eine INSERT-Query ab, erzeugt also einen neuen Spieler-Eintrag in der Tabelle. Die Syntax ist dieselbe wie bei orm_select:

    orm_insert(Player[playerid][ORM_ID], "OnPlayerRegister", "d", playerid);
    // ...
    forward OnPlayerRegister(playerid);
    public OnPlayerRegister(playerid)
    {
    printf("Spieler %s hat sich registriert und hat die ID %d.", Player[playerid][Name], Player[playerid][ID]);
    }

    Wie man sehen kann, wird der "Player[playerid][ID]"-Variable automatisch die neue Datenzeilen-ID aus der Tabelle zugewiesen. Dies passiert immer sobald solch eine ORM-Query abgeschickt wurde. Jedoch muss, bevor orm_insert aufgerufen wird, die richtige Variable als Schlüssel mit orm_setkey markiert sein.


    Okay, soweit sollte klar sein, wie man Daten abfrägt (orm_select) und hinzufügt (orm_insert). Aber wie werden schon existierende Daten in der Tabelle aktualisiert? Dazu muss orm_update verwendet werden. Diese Funktion generiert eine UPDATE-Query mit allen aktuellen Werten der registrierten Variablen.
    Ein Beispiel: Der Spieler "Spieler1" hat die ID 65 (nicht die playerid, die ID in der Tabelle), ist Level 4, hat 54634$ und ist irgendwo in Los Santos. In diesem Fall würde orm_update diese Query generieren:

    SQL
    UPDATE `players` SET `Name`='Spieler1', `Money`='54634', `Level`='4', `PosX`='745.231', `PosY`='-967.1425', `PosZ`='14.2543' WHERE `ID`='65'


    Wir können nun fast alle wichtigen Arten von Queries generieren, nur die DELETE-Query fehlt. Dafür gibt es auch eine Funktion, orm_delete. Wie man sich bereits denken kannst, generiert diese Funktion eine DELETE-Query. In unserem Beispiel würde es folgende Query generieren und absenden:

    SQL
    DELETE FROM `players` WHERE `ID`='65'


    Anders als orm_update hat orm_delete einen extra (optionalen) Parameter namens "clearvars". Wenn dieser auf 'true' gesetzt wird, wird orm_delete nicht nur den Tabellen-Eintrag löschen, sondern auch alle regestrierten Variablen auf 0 setzen (auch die Schlüssel-Variable).



    In diesem Tutorial wurde bis jetzt die ganze Zeit als Beispiel ein User-System genommen. Das heißt aber nicht, dass man sich nur auf solch ein System beschränken soll. Man kann jede Daten managen die man will, zum Beispiel bei einem Haus-System oder Fahrzeug-System. Hier ist ein Beispiel-Script das zeigt, wie man Fahrzeug-Daten mit dem ORM-System laden könnte:

    new SQL = -1;


    enum e_Vehicle
    {
    ORM:ORM_ID,
    VID,
    ID,
    ModelID,
    Color1,
    Plate[32],
    Float:Pos[4],
    };
    new Vehicle[MAX_VEHICLES][e_Vehicle];



    public OnGameModeInit()
    {
    mysql_log();

    SQL = mysql_connect("127.0.0.1", "root", "test", "pass");

    //lade Fahrzeuge
    mysql_tquery(SQL, "SELECT * FROM `vehicles`", "OnVehiclesLoad", "");
    return 1;
    }



    forward OnVehiclesLoad();
    public OnVehiclesLoad()
    {
    for(new r=0; r < cache_num_rows(); ++r) {
    new ORM:ormid = Vehicle[r][ORM_ID] = orm_create("vehicles");


    orm_addvar_int(ormid, Vehicle[r][ID], "ID"); //das ist unser Schlüssel
    orm_setkey(ormid, "ID"); //hier legen wir ihn auch als Schlüssel fest
    orm_addvar_int(ormid, Vehicle[r][ModelID], "ModelID");
    orm_addvar_int(ormid, Vehicle[r][Color1], "Color1");
    orm_addvar_string(ormid, Vehicle[r][Plate], 32, "Plate");
    orm_addvar_float(ormid, Vehicle[r][Pos][0], "PosX");
    orm_addvar_float(ormid, Vehicle[r][Pos][1], "PosY");
    orm_addvar_float(ormid, Vehicle[r][Pos][2], "PosZ");
    orm_addvar_float(ormid, Vehicle[r][Pos][3], "PosA");


    orm_apply_cache(ormid, r);


    Vehicle[r][VID] = CreateVehicle(Vehicle[r][ModelID], Vehicle[r][Pos][0], Vehicle[r][Pos][1], Vehicle[r][Pos][2], Vehicle[r][Pos][3], Vehicle[r][Color1], -1, -1);
    }


    return 1;
    }

    Was macht dieser Code?
    Er sendet eine normale Query (die in OnGameModeInit), welche alle Daten aus der Fahrzeug-Tabelle holt. Anschließend iteriert er durch die Tabellen-Reihen im "OnVehiclesLoad"-Callback. Für jede Tabellen-Reihe wird eine ORM-Instanz erstellt und dazugehörige Variablen registriert. Dann wird eine Funktion benutzt, die bislang nicht besprochen wurde: orm_apply_cache. Diese Funktion nimmt die derzeitige, aktive Cache und schaut ob in der Reihe mit dem Reihen-Index "r" Felder mit den registrierten Feldnamen sind (also "ID", "ModelID", "Color1" usw.). Wenn ja, wird der Wert dieses Feldes genommen und der jeweiligen Variable zugewiesen.


    Ich hoffe dieses Feature und Tutorial wird als nützlich erachtet.


    Danke an AndreT aus dem englischen SA-MP Forum für den Großteil dieses Tutorials und Kritiken/Anregungen zum ORM-System.
    Auch ein Dank an BigETI aus dem offiziellen deutschen SA-MP Forum für Korrekturen, Tipps und Anregungen zum Tutorial.

    Ich achte schon drauf, dass der Code über die Releases hinweg kompatibel bleibt. Leider gab es trotzdem ein paar Änderungen, z. B. hat mysql_format einen neuen Parameter in R27 oder so bekommen und mysql_ping wurde entfernt. Aber ansonsten kannst du R8 direkt mit R31 ersetzen und es sollte klappen wie gewohnt.

    Dann benutz es so:

    Code
    sudo apt-get update
    sudo apt-get upgrade


    Am besten du lädst dir den SAMP-Linux-Server mit dem "wget"-Befehl direkt über die Konsole runter anstatt es erst auf deinen PC zu laden, es dort zu entpacken und auf den Server per FTP zu laden.
    Wenn es immer noch crasht, wird es warscheinlich an einem der Plugins liegen die du benutzt. Versuche dann, die Plugins zu erneuern.

    Debian 7 unterstützt das MySQL 5.1 Paket nicht mehr (das Paket, mit dem das Plugin kompiliert wurde). Damit das Plugin bei dir läuft, musst du es selbst compilen oder du wartest bis R33 draußen ist (ab dann werde ich Linux-Versionen für verschiedene MySQL Versionen releasen, einmal für 5.1 und einmal für 5.5).