Difference between revisions of "Debug Scripts"

From the Oblivion ConstructionSet Wiki
Jump to navigation Jump to search
imported>AdiBeiElderScrolls
imported>Shademe
(Made note of RuDE)
 
(32 intermediate revisions by 8 users not shown)
Line 1: Line 1:
The debug process with CS is quite time consuming and the provided tools are not really satisfying.
Script debugging can be made easier with the use of [[:Category: RuntimeDEbugger|RuntimeDEbugger]], an OBSE plugin. It enables users to debug scripts as they execute in-game.  


So here are some hints, how to debug scripts in case you have unexpected results
General hints for debugging scripts:


===[[Script Processing]]===
===[[Script Processing]]===
First of all, you should take a look at the [[Script Processing]] article, to get a basic understanding, how the different script types are working.
First of all, you should take a look at the [[Script Processing]] article to get a basic understanding of how the different script types work.


Remember, that only quest scripts are considered to run constantly in a defined schedule.  
Remember that only quest scripts are considered to run constantly in a defined schedule.  


Object scripts may run every frame, but they also may not execute for quite a while, if player is not near. Even there are cases, where AI packages prevent script running. This is true for Follow-Packages if they check on conditions related to script variables in the same object.
Object scripts may run every frame, but they also may not execute for quite a while if player is not near. There are even cases where AI packages prevent script running. This is true for Follow-Packages if they check on conditions related to script variables in the same object and these conditions are satisfied.


To find out, if your script is executed, simply put a code like the following into your script and control it afterwards ingame
To find out if your script is executed, simply put a code like the following into your script and control it afterwards ingame:


<pre>; use long, if you want to watch the frames  
<pre>; use long, if you want to watch the frames  
Line 24: Line 24:


===Control variable content===
===Control variable content===
There are different ways, to control the current content of your variables.
There are different ways to control the current content of your variables.


The most convenient and reliable way is using the [[:Category:Console Functions|Console Functions]], especially '''prid''' and '''tst''' or '''sv'''. You always will get the real current content.
The most convenient and reliable way is using the [[:Category:Console Functions|Console Functions]], especially '''prid''' and '''tst''' or '''sv'''. You will always get the real current content.


So, first activate or focus the object, you want to examine (talk to your NPC, open the book ...) then open the console. You now will see in the upper part of the screen the '''formID''' of your object. If not, point to the object with your mouse.
So, first activate or focus the object you want to examine (talk to your NPC, open the book ...) then open the console. You will now see in the upper part of the screen the '''formID''' of your object. If not, left-click the object with your mouse.


The '''prid''' command will change to any object given, so if you want to examine your NPC script and your NPC has shown a '''formID''' of 0b002089 you simply enter:
If the object you want to examine cannot be left-clicked (because it's in a different cell, or disabled), you can select with the '''prid''' command if you know its form ID. The '''prid''' command will change to any object given, so if you want to examine your NPC script and your NPC has shown a '''formID''' of 0b002089 you simply enter:


<pre>prid 0b002089</pre>
<pre>prid 0b002089</pre>
Line 38: Line 38:
<pre>sv</pre>
<pre>sv</pre>


If you use many variables, you may only see the last ones, that have been declared.
If you use many variables, you may only see the last ones. In this case, press Page-Up to view the rest of the list.


To get a special variable content, use the '''Show/tst''' command. So if you want to control how much times the script has executed until now, if you have set a FrameRuns variable, you just type:
To get a special variable content, use the '''Show/tst''' command. So if you want to control how much times the script has executed until now, if you have set a FrameRuns variable, you just type:
Line 44: Line 44:
<pre>show FrameRuns</pre>
<pre>show FrameRuns</pre>


If that variable is not getting bigger and bigger everytime you leave the console and enter it again, you may just have the case, that some AI package is preventing your script from running.
If that variable is not getting bigger and bigger everytime you leave the console and enter it again, you may just have the case that some AI package is preventing your script from running.


'''Take care''', if you use [[Message]] or [[MessageBox]] functions inside of your script!
To see all of the variables within a particular quest, use '''ShowQuestVariables''' (or '''sqv''' for short):


The delay of the [[Message]] function will cause, that the displayed values are not the current ones. Moreover, the first time every message is executed in the script, it will be delayed. Every further execution (next frame) leads to messages running at the top of screen to fast to follow. To make sure, that you can watch all messages for every frame, use some dialog function like [[ShowEnchantment]] at the end of the code block to examine.
<pre>sqv QuestID</pre>


But always keep in mind, that the values shown in the message, are '''NOT''' the current values.
'''Take care''' if you use [[Message]] or [[MessageBox]] functions inside of your script!


[[MessageBox]] behaves in another way. If you want to have different message boxes, that pop up to show some variable content, you may end up with having only the last defined message box shown and all other ignored. ''Don't ask me why, but it seems to work this way''
[[Message]] can be useful for showing which segment of code is executing, or the value of a variable at a particular moment, but because messages always stay on the screen for one second, consecutive messages will be delayed. This means the value of a variable displayed in a message may have changed before the message gets displayed. OBSE's [[PrintToConsole]] function can make debug messages easier to use.
 
[[MessageBox]] behaves in another way. If you want to have different message boxes that pop up to show some variable content, you may end up with having only the last defined message box shown and all other ignored. ''Don't ask me why, but it seems to work this way''


At least [[MessageBox]] gives you the current values in the script.
At least [[MessageBox]] gives you the current values in the script.


===Find bugs not reported by CS or Oblivion===
===Find bugs not reported by CS or Oblivion===
CS usually gives you some hints, when you save a script. But this is not reliable at all. It only reports errors, where you try to access objects, that definitely do not exist.
CS usually gives you some hints when you save a script. But this is not reliable at all. It only reports syntax errors - for instance, referring to objects which don't exist or are not persistent, or mismatched if-endif blocks.


You still can make syntax errors, that are not discovered or use functions, that work as a return statement.
You still can make syntax errors that are not discovered or use functions that work as a return statement.


Ingame you won't get a message about a problem, except that your script seems not to do, what it should do. But if the interpreter for the script finds some errors, it just will cancel script execution at the point, where the error is found.
Unfortunately, Oblivion does not provide any error messages within the game itself. If it encounters an error within a script, it will immediately terminate that script without notifying you. That's why it's useful to have a variable in your script that increments every frame - by checking that variable in the console, you can determine if your script has stalled.


It took me a whole day to find out, why my script was not executed. And the reason was, that I mixed the commands [[GetCurrentAIPackage]] and [[GetIsCurrentPackage]].
Here's an example: It took me a whole day to find out why my script was not executed. And the reason was that I mixed the commands [[GetCurrentAIPackage]] and [[GetIsCurrentPackage]].


So the statement
So the statement
Line 85: Line 87:
</pre>
</pre>


But the script saved without any problems and ingame I didn't get a message, that something went wrong.
But the script saved without any problems and ingame I didn't get a message that something went wrong. This is because the compiler frequently ignores extra parameters. For instance, these lines, while incorrect, compile fine:
 
<pre>disable objectID 1
PickIdle My name is Bob</pre>
 
:And in fact, thats what's also true. But the GetCurrentAIPackage worked this time like a return function ... so it didn't execute any code above the first occurrence of GetCurrentAIPackage due to not using it correctly in a compare statement ... but using a valid package name ... just try (the package doesn't need to be related to the NPC)
 
<pre>GetCurrentAIPackage "ValidPackageName"
PickIdle My name is Bob</pre>
 
:Or
 
<pre>If ( GetCurrentAIPackage "ValidPackageName" )
  PickIdle My name is Bob
EndIf</pre>
 
:You may want to use the following test sequence with an NPC not related to the used package to verify the problem. You will notice, that the script can't be saved, if no valid package name is given. Also, it may happen, that the script afterwards won't execute anymore. In my case, FramesRun stayed at value 1, even if talking to the NPC - see details of my configuration below.
 
<pre>short FramesRun
Begin GameMode
 
Set FramesRun To FramesRun+1
Message "Test before GetCurrentAIPackage"
 
If ( GetCurrentAIPackage aaaCreatureExterior1500 )
  Message "Test aaaCreatureExterior1500 package name"
EndIf
 
Message "Still alive"
 
... other code
End</pre>
 
:So you see, some functions (especially that one's that can also work like a return function) may have unwanted behavior, not foreseen and others don't!
 
:Details about configuration: Using localized german version, with Update German V1.1, Plugin Oblivion Deutsch 3.0, BTMod 2.20
 
===Ingame starting debug messages at defined points===
If you want to evoke messages or messageboxes at defined point, it helps, if you define a debugOn variable in the script and set it via general topic or quest topic.


In this cases, it is recommended to use the [[Message]] function. Put a message after each code block to see, where the script stops execution, just like
All code inside would look like:


<pre>If WhatEverCondition1
<pre>short debugOn
  ... your code
... your code
If debugOn
  Message "whatever you want to say %.0f" MyVar
EndIf
EndIf
Message "WhatEverCondition1 executed"
... your code
If WhatEverCondition2
If debugOn
  ... your code
  Message "whatever you want to say %.0f" MyVar2
EndIf
EndIf
Message "WhatEverCondition2 executed"</pre>
... and so on
; and if you want to have the messages for each frame
; slowly passing by, you add a ShowEnchantment at the
; beginning or the end of the GameMode block, like
If debugOn
  ShowEnchantment
EndIf</pre>
 
You may also want to start the debug session on a defined point in your code. Nothing easier than that:
 
<pre>short debugOn
; save setting, for avoiding to be overruled by topics
; comment it out, if necessary
Set debugOn to 0
... your code
If debugOn
  Message "whatever you want to say %.0f" MyVar
EndIf
... your code
If YourConditionBlockToInspect
  Set debugOn to 1
  ; get from here on all your messages
  If debugOn
    Message "whatever you want to say %.0f" MyVar2
  EndIf
  ; and if you want to have the messages for each frame
  ; slowly passing by, you add a ShowEnchantment at the
  ; very end of the inspection block this time, like
  ShowEnchantment
  ; I didn't check in all cases, if ShowEnchantment works
  ; like a return function (Usually it works fine and not
  ; as a return function).  But be aware of scripts that 
  ; may not execute further than this point or comment it out ;)
  ; now stop getting messages for this frame
  Set debugOn to 0
EndIf
... and so on
</pre>
 
Alternatively, by using [[Oblivion Script Extender]], debug messages can be logged directly to the console through the [[PrintToConsole]] function. The messages printed thus offer many advantages, the prime being non-overlapping messages.
 
Messages displayed using [[Message]] and [[MessageEx]] can overlap with successive entries, thereby not displaying what's supposed to be.
 
In your final version you should remove any debug message and if debugOn blocks, as you deal with an interpreter. So the more statements you have in your code, the more time the script execution will take.


--[[User:AdiBeiElderScrolls|AdiBeiElderScrolls]] 07:58, 13 November 2006 (EST)


[[Category:Scripting]]
[[Category:Scripting]]

Latest revision as of 11:29, 25 February 2011

Script debugging can be made easier with the use of RuntimeDEbugger, an OBSE plugin. It enables users to debug scripts as they execute in-game.

General hints for debugging scripts:

Script Processing[edit | edit source]

First of all, you should take a look at the Script Processing article to get a basic understanding of how the different script types work.

Remember that only quest scripts are considered to run constantly in a defined schedule.

Object scripts may run every frame, but they also may not execute for quite a while if player is not near. There are even cases where AI packages prevent script running. This is true for Follow-Packages if they check on conditions related to script variables in the same object and these conditions are satisfied.

To find out if your script is executed, simply put a code like the following into your script and control it afterwards ingame:

; use long, if you want to watch the frames 
; for a longer time period
short FramesRun

Begin GameMode
Set FramesRun To FramesRun + 1

... your code

End

Control variable content[edit | edit source]

There are different ways to control the current content of your variables.

The most convenient and reliable way is using the Console Functions, especially prid and tst or sv. You will always get the real current content.

So, first activate or focus the object you want to examine (talk to your NPC, open the book ...) then open the console. You will now see in the upper part of the screen the formID of your object. If not, left-click the object with your mouse.

If the object you want to examine cannot be left-clicked (because it's in a different cell, or disabled), you can select with the prid command if you know its form ID. The prid command will change to any object given, so if you want to examine your NPC script and your NPC has shown a formID of 0b002089 you simply enter:

prid 0b002089

If you have not too much variables in the script, than you can use the ShowVars/sv command to show the content of all variables. Simply type

sv

If you use many variables, you may only see the last ones. In this case, press Page-Up to view the rest of the list.

To get a special variable content, use the Show/tst command. So if you want to control how much times the script has executed until now, if you have set a FrameRuns variable, you just type:

show FrameRuns

If that variable is not getting bigger and bigger everytime you leave the console and enter it again, you may just have the case that some AI package is preventing your script from running.

To see all of the variables within a particular quest, use ShowQuestVariables (or sqv for short):

sqv QuestID

Take care if you use Message or MessageBox functions inside of your script!

Message can be useful for showing which segment of code is executing, or the value of a variable at a particular moment, but because messages always stay on the screen for one second, consecutive messages will be delayed. This means the value of a variable displayed in a message may have changed before the message gets displayed. OBSE's PrintToConsole function can make debug messages easier to use.

MessageBox behaves in another way. If you want to have different message boxes that pop up to show some variable content, you may end up with having only the last defined message box shown and all other ignored. Don't ask me why, but it seems to work this way

At least MessageBox gives you the current values in the script.

Find bugs not reported by CS or Oblivion[edit | edit source]

CS usually gives you some hints when you save a script. But this is not reliable at all. It only reports syntax errors - for instance, referring to objects which don't exist or are not persistent, or mismatched if-endif blocks.

You still can make syntax errors that are not discovered or use functions that work as a return statement.

Unfortunately, Oblivion does not provide any error messages within the game itself. If it encounters an error within a script, it will immediately terminate that script without notifying you. That's why it's useful to have a variable in your script that increments every frame - by checking that variable in the console, you can determine if your script has stalled.

Here's an example: It took me a whole day to find out why my script was not executed. And the reason was that I mixed the commands GetCurrentAIPackage and GetIsCurrentPackage.

So the statement

If ( GetCurrentAIPackage "00MyPackage1" )
   Set PackageLoaded To 1
ElseIf ( GetCurrentAIPackage "00MyPackage2" )
   Set PackageLoaded To 2
...

caused the interpreter to leave the script, whenever it reached this code block. If you wonder about the "00MyPackage", the apostrophes are recommended, whenever your EditorIDs start with a number. I used two zeros to have my objects always listed at the top

And it was right, because what I meant to do was the following

If ( GetIsCurrentPackage "00MyPackage1" )
   Set PackageLoaded To 1
ElseIf ( GetIsCurrentPackage "00MyPackage2" )
   Set PackageLoaded To 2
...

But the script saved without any problems and ingame I didn't get a message that something went wrong. This is because the compiler frequently ignores extra parameters. For instance, these lines, while incorrect, compile fine:

disable objectID 1
PickIdle My name is Bob
And in fact, thats what's also true. But the GetCurrentAIPackage worked this time like a return function ... so it didn't execute any code above the first occurrence of GetCurrentAIPackage due to not using it correctly in a compare statement ... but using a valid package name ... just try (the package doesn't need to be related to the NPC)
GetCurrentAIPackage "ValidPackageName"
PickIdle My name is Bob
Or
If ( GetCurrentAIPackage "ValidPackageName" )
  PickIdle My name is Bob
EndIf
You may want to use the following test sequence with an NPC not related to the used package to verify the problem. You will notice, that the script can't be saved, if no valid package name is given. Also, it may happen, that the script afterwards won't execute anymore. In my case, FramesRun stayed at value 1, even if talking to the NPC - see details of my configuration below.
short FramesRun
Begin GameMode

Set FramesRun To FramesRun+1
Message "Test before GetCurrentAIPackage"

If ( GetCurrentAIPackage aaaCreatureExterior1500 )
   Message "Test aaaCreatureExterior1500 package name"
EndIf

Message "Still alive"

... other code
End
So you see, some functions (especially that one's that can also work like a return function) may have unwanted behavior, not foreseen and others don't!
Details about configuration: Using localized german version, with Update German V1.1, Plugin Oblivion Deutsch 3.0, BTMod 2.20

Ingame starting debug messages at defined points[edit | edit source]

If you want to evoke messages or messageboxes at defined point, it helps, if you define a debugOn variable in the script and set it via general topic or quest topic.

All code inside would look like:

short debugOn
... your code
If debugOn
   Message "whatever you want to say %.0f" MyVar
EndIf
... your code
If debugOn
   Message "whatever you want to say %.0f" MyVar2
EndIf
... and so on
; and if you want to have the messages for each frame
; slowly passing by, you add a ShowEnchantment at the 
; beginning or the end of the GameMode block, like 
If debugOn
   ShowEnchantment
EndIf

You may also want to start the debug session on a defined point in your code. Nothing easier than that:

short debugOn
; save setting, for avoiding to be overruled by topics
; comment it out, if necessary
Set debugOn to 0
... your code
If debugOn
   Message "whatever you want to say %.0f" MyVar
EndIf
... your code
If YourConditionBlockToInspect
  Set debugOn to 1
  ; get from here on all your messages
  If debugOn
     Message "whatever you want to say %.0f" MyVar2
  EndIf
  ; and if you want to have the messages for each frame
  ; slowly passing by, you add a ShowEnchantment at the 
  ; very end of the inspection block this time, like 
  ShowEnchantment
  ; I didn't check in all cases, if ShowEnchantment works 
  ; like a return function (Usually it works fine and not 
  ; as a return function).  But be aware of scripts that  
  ; may not execute further than this point or comment it out ;)
  ; now stop getting messages for this frame
  Set debugOn to 0
EndIf
... and so on

Alternatively, by using Oblivion Script Extender, debug messages can be logged directly to the console through the PrintToConsole function. The messages printed thus offer many advantages, the prime being non-overlapping messages.

Messages displayed using Message and MessageEx can overlap with successive entries, thereby not displaying what's supposed to be.

In your final version you should remove any debug message and if debugOn blocks, as you deal with an interpreter. So the more statements you have in your code, the more time the script execution will take.