GUI Scripting: Debugging

From The DarkMod Wiki
Jump to navigationJump to search

This page is part of a series. See GUI Scripting Language for overview.

Introduction

This article reviews certain changes you can make to your GUI (and/or associated .script functions) to provide insights into problems. It is not unusual for the parsing of a GUI to fail at some location. Sometimes, the parser will report information to the console that makes it easy to find that location and/or the cause. Other times, detective work is needed, trying to see how far parsing got.

Power developers with extensive experience can debug by setting breakpoints in the C++ code; main menu designers have "set 'cmd' log" available to give insights. Mere-mortal mappers, without recourse to these techniques, will rely more on trial and error, but with help from methods like these here.

See also GUI Scripting: Tools, particularly about reloading a GUI during testing.

Showing Debug Information on the Desktop

This is discussed in the context of a .gui file for a full-screen, Desktop-style overlay, like an HUD or main-menu presentation. [Not tested for an in-world surface.]

Debug-specific Properties

Properties "showtime" and "showcoords" can be added to any guiDef. Each displays output when their guiDef is active. In an HUD context, the text will be placed near the upper left screen corner. They can be used simultaneously, with showtime info to the right.

Showtime bool

Display the guiDef time (e.g., that affects onTime handlers)? Default: 0 (false). Display format is, for instance, "0.4 seconds" (usually changing rapidly).

showtime 1

Showcoords bool

Display the guiDef origin coordinates? Default: 0 (false).

showcoords 1

Showcoords displays the guiDef’s "rect" first 2 coordinates (upper-left corner origin x & y). Recall these values are relative to a parent guiDef’s origin, NOT the absolute screen location. [Also, an attempt to numerically show the mouse cursor position is made but, as of TDM 2.10, mostly fails due to field clipping; bug report #6103 filed.]

Other Techniques

Adding a visual border to a guiDef

See a worked example in GUI Scripting: BindDef.

Adding an additional windowDef to display text values

As a child of your Desktop windowDef, you could add:

windowDef Debug {
   rect 0, 0, 640, 10
   visible 1
   text ""
}

This will display a line of text (in the default white font) in a known place at the top of the screen. Then, elsewhere in your GUI’s event handlers, you could invoke that:

windowDef SomeChild { 
   ...
   onTime 0 {
     transition "matcolor" "1 1 1 0.7" "0.5 0.5 1 0.7" 200;
     set "Debug::Text" "Time 0 – Normal to Blue Tint";
   }
   onTime 400 {
     transition "matcolor" "0.5 0.5 1 0.7" "1 1 1 0.7" 200;
     set "Debug::Text" "Time 400 – Blue Tint to Normal";
   }
   onTime 800 {
     resetTime 0;
   }
}

This works for string literals as shown. As to displaying other types of information, consider these cautions:

  • String formatting and concatenation are not available to the set command. But you could deploy more than one debugging windowDef with differing rects, e.g., for prompt and value.
  • A non-literal source for a set command should be prefixed with $. In addition, it seems to need a "::" style prefix, even if for a local property. So:
windowDef SomeChild { 
   background "gui::myChildBackground" // Path to background comes from associated .script
   ...
   onTime 0 {
     ...
     set "Debug::Text" "$SomeChild::background"; // works, shows passed value.
     set "Debug::Text" "$background"; // Doesn’t work
   }
   ...
}
  • A property likely needs to be a register to have an accessible value within an event handler. So if nothing is displayed, check if the source property you’re using is a register.
  • Int and float registers and float user variables likely will display correctly as numbers. Example:
set "Debug::text" "$SomeChild::visible"; // works, shows 1
  • When displaying numbers, a float register and an int (including bool) register will display different appropriate formats. Float user variables will always show a float format, e.g., "1.000000".
  • Vectors will fail, often crash

Logging Debug Information

Main Menu System Development

Main menu designers have logging available, e.g.:

set "cmd" "log 'BriefingStateEnd called.'";

For details, see GUI Scripting: Parsing of Set 'Cmd'.

FM Development - Logging via the Console

In the absence of "set 'cmd' log" capability, it is possible to cobble together something similar (but shakier), using a GUI::Parameter and polling script. The script will write to the console window, and so appear in routine console logging.

Suppose you want to know if/when this event is firing in a guiDef:

    onTime 800 {
      resetTime 0;
      set "gui::Println" "reset";
    }

Here, the string "reset" is passed to the polling script, through an invented "gui::Println".

Within the <FM>.script Alone

If <myFM>.script is the only .script file in your FM, it is easy to pass the needed handle as a file-level variable, thusly:

#define NO_HANDLE_YET -99
float _handle = NO_HANDLE_YET;

void poll_and_print() {
    string s;
    while(1) {
        sys.wait(0.01);
        if(_handle == NO_HANDLE_YET)
            continue;
        s = $player1.getGuiString(_handle, "Println");
        if(s == "")
            continue;
        sys.println(s);
        $player1.setGuiString(_handle, "Println", "");
    }
 }

void main() {
     sys.waitFrame();
     thread poll_and_print();
}

Then, elsewhere in your script, when you first get the appropriate overlay handle, copy it to _handle.

With a Script Object in a Separate File

If the script function where the GUI overlay handle becomes available is in a different file that <FM>.script, then either hard-code the handle number (risky) or pass it in a custom CVar, instead of using a file-level variable. In the <FM>.script:

#define NO_HANDLE_YET -99

void poll_and_print()
{
    float _handle;
    string s;
    while(1)
    {
        sys.wait(0.01);
        _handle = sys,getcvar("mydebughandle"); // initialized by main()
        if(_handle == NO_HANDLE_YET)
            continue;
        s = $player1.getGuiString(_handle, "Println");
        if(s == "")
            continue;
        sys.println(s);
        $player1.setGuiString(_handle, "Println", "");
   }
}

void main()
{
    sys.waitFrame();
    sys.setcvar("mydebughandle", NO_HANDLE_YET); // initialize
    thread poll_and_print();
}

Then, when your script object in a different .script file gets the appropriate overlay handle (by various means, but here shown as "_handle2"), update the cvar:

sys.setcvar("mydebughandle", _handle2);

Caveats

  • Within poll_and_print...
    • it is assumed the GUI is for an overlay and thus $player1 is its userEntity.
    • In the while loop, you have to have some wait time (to avoid a "runaway loop" abort), but it has to be relatively minimal, to minimize the likelihood that a posting to gui::Printlng will be overwritten by a subsequent posting before it could be displayed to console.
  • As mentioned earlier in the HUD display context, in the GUI, a "set" command for a string has really no string-formatting capabilities, not even simple concatenation. In some cases, you might need to pass multiple GUI::Parameters (strings or floats) and do any required string formatting on the .script side.