Debugnachrichten

  • Das hier ist kein Tutorial im Eigentlichen Sinne,sondern ein Tipp
    für alle die weiter kommen möchten mit ihren Scriptfähigkeiten indem sie Selbstständig
    ihre Fehler finden und beheben. Es soll euch helfen, eure Fehler im Code zu verstehen und zu beheben.


    Das Stichwort sind Debug-Notes oder einfach auch Debug-Nachrichten.
    Das sind Nachrichten bzw Texte, die ihr in euren Code einbaut, um nachzuvollziehen
    mit welchen Werten euer Script welchen Weg durchläuft ( Kein richtiger Weg, aber wir können
    uns die unzäglichen if / else if / else Bedinungen,Schleifen,Codeblöcke usw doch so vorstellen, oder ;]? )
    .
    Ich verwende sie persönlich sehr oft,denn es kommt immer mal wieder vor das einem Fehler unterlaufen.


    Debug-Notes werden verwendet, um den Grund zu finden, wieso etwas
    nicht so funktioniert, wie man es meistens möchte.


    Nehmen wir eine Ganz simple Abfrage, um zu prüfen ob iInput gleich iValue ist.
    Das Beispiel ist jetzt ziemlich banal, aber es soll nur die grobe Form darstellen wie
    es funktionieren soll.
    iInput könnte bspw eine globale Variable sein, die sich merkt, welcher Spieler zuletzt
    gejoint ist.



    new
    iInput = 14,
    iValue = 4;
    if(iInput == iValue) {
    // Code
    // Noch mehr Code
    // Code Ende
    }

    Nun gut. Sollte die Abfrage scheitern, hätten wir keine Informationen darüber, wieso es dazu kommt.
    Da wir in der Abfrage zwei Integer-Werte vergleichen,sollten wir uns diese
    doch am besten ausgeben lassen, falls die Abfrage false ergibt.



    new
    iInput = 14,
    iValue = 4;
    if(iInput == iValue) {
    // Code
    // Noch mehr Code
    // Code Ende
    }
    else {
    printf("DEBUG: iValue %d iInput %d",iValue,iInput);
    }

    Man kann auch direkt über die if() Abfrage die Debug-Notes einfügen.Ist Gewöhnungssache.

    new
    iInput = 14,
    iValue = 4;
    printf("DEBUG: iValue %d iInput %d",iValue,iInput);
    if(iInput == iValue) {
    // Code
    // Noch mehr Code
    // Code Ende
    }

    Das ist jetzt ein völlig frei erfundenes Beispiel, dass nur den Grundaufbau zeigt.Natürlich
    würde es in eurem Script viel weiter gehen und ihr solltet dann auch immer weitere Debug-Infos
    für euch hinzufügen,um dem Fehler auf die Spur zu kommen.



    public OnPlayerDeath(playerid,killerid,reason) {
    PlayerDeaths[playerid]++;
    PlayerKills[killerid]++;
    // Mehr Code
    printf("PlayerDeaths[%d] %d PlayerKills[%d] %d",playerid,PlayerDeaths[playerid],killerid,PlayerKills[killerid]);
    return 1;
    }
    Auch ein ziemlich simpler Code.Ich würde einfach mal tippen,dass Einigen nicht auffällt, dass
    dieser Code unter Umständen zu einem Stopp im Script kommt.
    Der Fehler kommt sehr häufig vor,ja, ich habe das manchmal auch übersehen.
    Angenommen man wird von einem Mitspieler getötet der die ID 15 hat.Man selber ist ID 4

    public OnPlayerDeath(playerid,killerid,reason) {
    PlayerDeaths[4]++;
    PlayerKills[15]++;
    // Mehr Code
    printf("PlayerDeaths[%d] %d PlayerKills[%d] %d",4,PlayerDeaths[4],15,PlayerKills[15]);
    return 1;
    }
    Alles Okay soweit.Der Code wird Fehlerfrei funktionieren.Was ist aber,wenn man Selbstmord begeht?
    ( Man muss ja immer alle Möglichkeiten durchtesten. )
    Dann ist die killerid INVALID_PLAYER_ID ( 0xFFFF ) oder auch 65535.Wir sind weiterhin ID 4.

    public OnPlayerDeath(playerid,killerid,reason) {
    PlayerDeaths[4]++;
    PlayerKills[65535]++; // index out of bounce! 65535 ist definitiv größer als MAX_PLAYERS
    // Wird nicht weiter ausgeführt - Mehr Code
    printf("PlayerDeaths[%d] %d PlayerKills[%d] %d",4,PlayerDeaths[4],15,PlayerKills[15]);
    return 1;
    }
    Jetzt würde man höchstwahrscheinlich erst gar nicht bis zum printf() im Code gelangen,da vorher
    mehr oder weniger der Code gestoppt wird. Jetzt bringt uns die Debug-Note leider nichts.
    Daher ist es oft noch hilfreich, einzelne Debug-Notes zu setzen um zu sehen, bis zu welcher Operation der Code Fehlerfrei läuft.
    Am besten ihr fügt auch eine Debug-Note ein,dass die Funktion überhaupt aufgerufen wurde.Dazu noch alle
    Parameter die übergeben werden.

    public OnPlayerDeath(playerid,killerid,reason) {
    printf("DEBUG: OnPlayerDeath( %d , %d , %d )",playerid,killerid,reason);
    PlayerDeaths[playerid]++;
    printf("DEBUG: 2");
    PlayerKills[killerid]++;
    printf("DEBUG: 3");
    // usw usf
    return 1;
    }
    Anhand der Debug-Notes solltet ihr auswerten können,wo der Fehler liegt. Natürlich muss
    man sich jetzt auch selber bischen anstrengen und den Fehler finden, doch so ist es definitiv
    leichter für jeden seine Fehler selber zu finden, ohne bei jeder Fehlermeldung nach Hilfe zu nachzufragen.


    In der Konsole ( serverlog ) wird ausgegeben bei Selbstmord:

    Zitat


    DEBUG: OnPlayerDeath( 4, 65535, 0 )
    DEBUG: 2


    Jetzt sollte wohl klar sein,dass es an folgender Zeile liegt:
    PlayerKills[killerid]++;


    Den letzten Schritt und den Fehler fixen müsst ihr aber selber machen. Ich
    geb den Code aber trotzdem mal richtig an:

    public OnPlayerDeath(playerid,killerid,reason) {
    printf("DEBUG: OnPlayerDeath( %d , %d , %d )",playerid,killerid,reason);
    PlayerDeaths[playerid]++;
    printf("DEBUG: 2");
    if( killerid != INVALID_PLAYER_ID ) {
    PlayerKills[killerid]++;
    printf("DEBUG: 3");
    }
    // usw usf
    return 1;
    }




    Nehmen wir ein weiteres Beispiel ( Aus dem Forum etwas gekürzt )

    // Folgende Variablen gibt es:
    //
    // new bool:Eingeloggt[MAX_PLAYERS];
    // new s[128];
    // weiteres:
    // #define DIALOG_REGISTER 5
    //
    // Es soll ein REGISTER Dialog erstellt werden.
    // Die Dialogid ist DIALOG_REGISTER ( 5 ) und es soll in dieses Fenster
    // ein Passwort eingegeben werden.
    // Der Code stand irgendwo hier im Forum ... ist nicht original von mir
    //
    public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
    {
    if(dialogid == DIALOG_REGISTER)
    {
    if(!Eingeloggt[playerid])
    {
    if(response)
    {
    if(!strlen(inputtext))
    {
    GetPlayerName(playerid,Name,sizeof(Name));
    format(s,sizeof(s),"acc/%s.ini",Name);
    if(!dini_Exists(s))
    {
    dini_Create(s);
    dini_IntSet(s,"Passwort",udb_hash(inputtext));


    }
    }


    }
    }
    }
    return 1;
    }
    Dem Geschultem Auge wird relativ schnell auffallen wo der Fehler liegt.Nicht schlimm wenn man es
    nicht sieht.
    Wir kommen also nicht dahinter wo jetzt der Fehler liegt,was machen wir also?


    Wir könnten jetzt natürlich einen Thread im Forum erstellen mit einem Threadtitel,
    der nicht gerade Aussagekräftig ist und einer sehr geringen Information zum eigentlichem Problem. Nach
    spätestens 1Stunden pushen wir und fragen uns,wieso uns bisher keiner geholfen hat.


    Oder,wir fangen an den Code mit Debug-Notes zu füllen um nachvollziehen zu können,bis vohin
    der Code abläuft.
    Nun testen wir das ganze nochmal um auch die Debug-Notes zu erhalten mit den Daten die wir danach auswerten.
    public OnDialogResponse(playerid, dialogid, response, listitem, inputtext[])
    {
    printf("OnDialogResponse(%d , %d , %d , %d , '%s')",playerid, dialogid, response, listitem, inputtext);
    printf("DEBUG: 1 - %d == %d ( dialogid == DIALOG_REGISTER )",dialogid,DIALOG_REGISTER);
    if(dialogid == DIALOG_REGISTER)
    {
    printf("DEBUG: 2 - %d ( !Eingeloggt[playerid] )",!Eingeloggt[playerid]);
    if(!Eingeloggt[playerid])
    {
    printf("DEBUG: 3 - %d ( response )",response);
    if(response)
    {
    printf("DEBUG: 4 - %d '%s' ( !strlen(inputtext) inputtext)",!strlen(inputtext),inputtext);
    if(!strlen(inputtext))
    {
    printf("DEBUG: 5");
    GetPlayerName(playerid,Name,sizeof(Name));
    printf("DEBUG: 6 - '%s' ( Name )",Name);
    format(s,sizeof(s),"acc/%s.ini",Name);
    printf("DEBUG: 7 - '%s' ( s )",s);
    printf("DEBUG: 8 - %d ( !dini_Exists(s) )",!dini_Exists(s));
    if(!dini_Exists(s))
    {
    printf("DEBUG: 9");
    dini_Create(s);
    printf("DEBUG: 10");
    dini_IntSet(s,"Passwort",udb_hash(inputtext));
    printf("DEBUG: 11");
    // usw.
    }
    }


    }
    }
    }
    return 1;
    }
    Haben wir das getan,sehen wir uns die Debug-Notes in der Log an.

    Zitat

    server_log.txt
    OnDialogResponse(0 , 5 , 1 , 0 , 'meinpasswort')
    DEBUG: 1 - 5 == 5 ( dialogid == DIALOG_REGISTER )
    DEBUG: 2 - 1 ( Eingeloggt[playerid] )
    DEBUG: 3 - 1 ( response )
    DEBUG: 4 - 0 'meinpasswort' ( !strlen(inputtext) inputtext)


    Wir wissen nun also bis wohin der Code abläuft anhand der Debug-Notes.
    Anscheinend geht es hier nicht mehr weiter
    if(!strlen(inputtext))
    Nun,schauen wir uns mal den Code genauer an. strlen() gibt die Länge des Strings zurück.
    In der Abfrage wollen wir aber überprüfen,ob die Länge des Strings 0 ist (!).Die Sache ist aber,dass
    wir als Passwort "meinpasswort" eingegeben haben.Die Länge ist also definitiv nicht 0. Sondern
    12 im Beispiel von "meinpasswort"


    Wir haben also den Fehler gefunden.Es liegt daran,dass wir fälschlicherweise in der Abfrage überprüfen,ob der
    String leer ist bzw keiner eingegeben wurde, anstatt zu schauen ob er überhaupt existiert
    if(strlen(inputtext))



    Natürlich,wozu ist das Ganze gut. Es soll dabei helfen, selbstständig seine Fehler zu finden anstatt immer
    von Anderen Hilfe in Anspruch zu nehmen. Klar ist das Forum dazu da, um euch bei Fehlern zu helfen,doch
    wäre es nicht besser, man schafft es auch alleine seine Fehler zu finden ? Falls man es wirklich nicht
    schafft seinen Fehler zu finden nachdem man es probiert hat durch die Debug-Notes, kann man immernoch nachfragen.




    Ich selber benutzen auch oft diese Technik um meine Fehler zu finden. Irgendwo schleicht sich doch bei
    jedem von uns der ein oder Andere Fehler ein, den wir nicht auf den ersten Blick sofort erkennen.
    Passend dazu habe ich mir vor langer Zeit auch eine kleine Funktion geschrieben,damit Ich mir
    Ingame die letzten Debug-Notes ansehen kann.
    Download < debugviewer >


    Include einfügen und an den Stellen,wo ihr eine Debug Nachricht einfügen wollt folgendes:

    Debug_AddEntry("Das ist ein %s mit %d Worten und pi %.2f", "Test",8,3.1415); // - Mit auto-format
    Debug_AddEntry("Das ist ein ein simpler Test",0); // - Falls kein format-Parameter,einfach 0 eintragen

    An der gewünschten Stelle noch
    Debug_ShowPanel(playerid)
    ,wenn ihr dem Spieler das Panel anzeigen wollt.




    Falls es noch Fragen,Anmerkungen oder Sonstiges gibt könnt ihr die natürlich gerne stellen :p

  • Ich selber benutzen auch oft diese Technik um meine Fehler zu finden. Irgendwo schleicht sich doch bei
    jedem von uns der ein oder Andere Fehler ein, den wir nicht auf den ersten Blick sofort erkennen.
    Passend dazu habe ich mir vor langer Zeit auch eine kleine Funktion geschrieben,damit Ich mir
    Ingame die letzten Debug-Notes ansehen kann.
    Download < debugviewer >


    SendClientMessage ist InGame ebenfalls recht nuetzlich, wenn man es nicht in Massen verwendet und nach dem debuggen entfernt, oder sich inetwa soetwas baut:
    #define DEBUG
    #ifdef DEBUG
    #define SendClientMessage_DEBUG SendClientMessage
    #define SendClientMessageToAll_DEBUG SendClientMessageToAll
    #else
    #define SendClientMessage_DEBUG //
    #define SendClientMessageToAll_DEBUG // - oder wenn man nicht auf die debug Nachrichten verzichten moechte, aber nicht fuer jeden sichtbar: #define SendClientMessageToAll_DEBUG(x, y) printf("%s", y)
    #endif
    Damit braucht man nur noch die DEBUG definition auskommentieren, wenn man das Script auf einem public server verwenden moechte.


    PS.: Sollte jetzt keine Kritik an deinem debugviewer sein, sondern nur ein Vorschlag wie man das Tutorial ein wenig erweitern kann - finde den debugviewer ganz gut, denn er scheint aenhlich einfach zu implementieren zu sein, wie ein Aufruf zu printf. ;)