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.
Zitatserver_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