IT/Guida al Debug
Scriptando ti capiteranno presto problemi che non si notano subito. Questa pagina ha lo scopo di elencare qualche strategia di base per trovare l'errore.
Console di debug
MTA possiede una console ingame per il debug. Puoi aprirla digitando debugscript x nella console, dove x è il livello di debug:
- 1: solo gli errori
- 2: gli errori e i warning
- 3: gli errori, i warning e le info
Perciò, digitando debugscript 3 tutti i messaggi saranno visibili, anche se il livello 2 è raccomandato per la maggior parte dei casi. Dovresti avere il debugscript attivato la maggior parte del tempo che scripti, ti aiuterà a trovare errori di digitazione, insieme ad altri piccoli problemi e a risolverli facilmente.
Esempio
Questo frammento di esempio ha due errori:
if (getClientName(player) == "Fedor") outputChatbox("DISGRACE") end
Quando lo script tenterà di caricare questa parte di codice, nella console di debug dovrebbe apparire qualcosa del genere:
Significa che c'è un errore di sintassi. Il debugscript mostra il percorso dello script che genera l'errore, in modo da conoscere la resource ('myResource' in questo caso) e ovviamente il nome dello script. Dopo il nome del file mostra la linea dell'errore e poi il tipo di errore. A questo punto è facile, abbiamo dimenticato il 'then':
if (getClientName(player) == "Fedor") then outputChatbox("DISGRACE") end
Ora lo script caricherà senza errori e sembrerà funzionare bene, ma quando un player di nome 'Fedor' raggiungerà questa parte dello script, il debugscript darà questo errore:
Significa che la funzione chiamata non esiste: è facilmente spiegabile, visto che il nome esatto della funzione è outputChatBox (con la B maiuscola):
if (getClientName(player) == "Fedor") then outputChatBox("DISGRACE") end
Ovviamente è solo un esempio, potrebbe esserci un'infinità di altre situazioni, ma dovreste esservi fatti un'idea.
Log del debug
Puoi anche attivare la creazione di un file log del debug modificando il file coreconfig.xml nella tua cartella MTA all'interno di quella di GTA. Devi cercare la riga seguente:
<debugfile/>
E rimpiazzarla con questa, scegliendo la path che preferisci, relativa alla cartella di GTA:
<debugfile>MTA\debugscript.log</debugfile>
Tutti i messaggi di debug da ora in poi verranno aggiunti al file log. Per bloccare questo processo, ripristina la riga precedente.
Strategie di debug
Esistono svariate strategie per trovare gli errori, la maggior parte delle quali prevede l'uso dei messaggi di debug, con informazioni differenti a seconda della situazione.
Funzioni utili
Prima di tutto ecco alcune funzioni comode per il debug.
- outputDebugString o outputChatBox per l'output di informazioni nella console di debug o in chatbox
- (in inglese) tostring() su una variabile per trasformarla in una stringa (per esempio se è una float)
- getElementType per verificare il tipo di un elemento di MTA
Creare messaggi di debug per verificare se, quando o quante volte è eseguita una parte di codice
Un esempio tipico potrebbe essere verificare se la sezione di codice dentro un if sia eseguita o meno. Per far questo, aggiungi un messaggio qualsiasi (che in seguito riconoscerai) nella sezione dentro l'if.
if (variabile1 == variabile2) then outputDebugString("if eseguito") -- fai qualsiasi cosa end
Un altro uso potrebbe essere verificare se il valore di una variabile è modficato. Prima cerca tutti i punti in cui questo valore è modificato e aggiungi un messaggio di debug proprio sotto di essi.
Creare messaggi di debug per verificare il valore di una variabile
Diciamo che vuoi creare un marker, ma non sembra essere nella posizione dove dovrebbe trovarsi. La prima cosa che potresti voler fare è controllare se la funzione createMarker è eseguita. Ma nel fare ciò, puoi anche verificare le variabili usate nel createMarker in un colpo solo.
outputChatBox(tostring(x).." "..tostring(y).." "..tostring(z)) createMarker(x,y,z)
Questo mostrerà le 3 variabili usate come coordinate per creare il marker. Assumendo che tu le 'legga' da un file map, puoi controllare in questo modo il loro valore. La funzione tostring() permette di unire con certezza i valori delle variabili in una stringa, anche se per esempio sono dei booleani.
Esempio
Immagina di creare una colshape da qualche parte e di volere che un'azione accada dopo che un player resta 10 secondi dentro di essa.
function colShapeHit(player) -- setta un timer per mostrare un messaggio (potrebbe ugualmente eseguire un'altra funzione) -- settare l'id del timer in una tabella, con il player come ''index'' colshapeTimer[player] = setTimer(outputChatBox,10000,1,"Il player è rimasto 10 secondi nella colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- elimina il timer quando un player esce dalla colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Quando un player entra nella colshape, il debugscript mostra il questo messaggio:
Significa che hai tentato di settare un index di una tabella che non esiste. Nell'esempio di sopra, accade quando viene settato l'id del timer nell'index della tabella. Dobbiamo verificare se la tabella esiste e, se non esiste, crearla.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- setta un timer per mostrare un messaggio (potrebbe ugualmente eseguire un'altra funzione) -- settare l'id del timer in una tabella, con il player come ''index'' colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- elimina il timer quando un player esce dalla colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Ora riceviamo un errore quando un player entra nella colshape, aspetta dieci secondi e poi la lascia:
A parte questo (ne riparleremo più tardi) tutto sembra funzionare bene. Se un player entra nella colshape, il timer è avviato, se resta dentro 10 secondi il messaggio viene mostrato e se esce il timer viene eliminato.
Un errore ancor meno visibile
Ma per qualche ragione il messaggio viene mostrato due volte se entri nella colshape mentre sei in un veicolo. Siccome sembra che una parte del codice sia eseguita due volte, aggiungiamo dei messaggi di debug per controllarlo.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- messaggio di debug outputDebugString("colShapeHit") -- setta un timer per mostrare un messaggio (potrebbe ugualmente eseguire un'altra funzione) -- settare l'id del timer in una tabella, con il player come ''index'' colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- messaggio di debug outputDebugString("colShapeLeave") -- elimina il timer quando un player esce dalla colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Ora ci rendiamo conto che entrambe le funzioni vengono eseguite due volte se siamo in un veicolo, ma solo una volta se siamo a piedi. Sembrerebbe che anche il veicolo chiami gli eventi legati alla colshape. Per confermare questa teoria, controlliamo la variabile player che dovrebbe contenere un player.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- messaggio di debug outputDebugString("colShapeHit "..getElementType(player) ) -- setta un timer per mostrare un messaggio (potrebbe ugualmente eseguire un'altra funzione) -- settare l'id del timer in una tabella, con il player come ''index'' colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- messaggio di debug outputDebugString("colShapeLeave "..getElementType(player) ) -- elimina il timer quando un player esce dalla colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
I messaggi di debug ci mostrano che un sola delle variabili player è davvero un player, l'altra è un veicolo. Visto che vogliamo che lo script reagisca solo quando un player entra nella colshape, aggiungiamo un if che bloccherà l'esecuzione della funzione se player not è un elemento player.
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeHit "..getElementType(player)) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeLeave "..getElementType(player)) -- kill the timer when the player leaves the colshape killTimer(colshapeTimer[player]) end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave)
Now the script should work as desired, but will still output the warning mentioned above. This happens because the timer we try to kill when a player leaves the colshape will not exist anymore when it reached the 10 seconds and is executed. There are different ways to get rid of that warning (since you know that the timer might not exist anymore and you only want to kill it if it is there). One way would be to check if the timer referenced in the table really exists. To do this, we need a little help function:
function isTimer(timer) local timers = getTimers() for k,v in ipairs(timers) do if (v == timer) then return true end end return false end
Which we will use when we kill the timer:
if (isTimer(colshapeTimer[player])) then killTimer(colshapeTimer[player]) end
So the complete working code would be:
function colShapeHit(player) if (colshapeTimer == nil) then colshapeTimer = {} end -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeHit "..getElementType(player)) -- set a timer to output a message (could as well execute another function) -- store the timer id in a table, using the player as index colshapeTimer[player] = setTimer(outputChatBox,10000,1,"The player stayed 10 seconds in the colshape!") end addEventHandler("onColShapeHit",getRootElement(),colShapeHit) function colShapeLeave(player) -- add a check for the element type if (getElementType(player) ~= "player") then return end -- add a debug message, with the element type outputDebugString("colShapeLeave "..getElementType(player)) -- kill the timer when the player leaves the colshape if (isTimer(colshapeTimer[player])) then killTimer(colshapeTimer[player]) end end addEventHandler("onColShapeLeave",getRootElement(),colShapeLeave) function isTimer(timer) local timers = getTimers() for k,v in ipairs(timers) do if (v == timer) then return true end end return false end