debugging in fteqcc.
double-click a .src file (select fteqccgui to open it with, if you've not already set up your file associations).
hit f7 to compile it.
hit f5 to start running it (first time it'll likely prompt for engine (pick some version of fte), and then the base directory, it'll guess the gamedir from the .src location and add -window to the commandline, because that's more sane).
in the gui:
hit f5 to resume normal running after hitting a breakpoint.
hit f7 to recompile - note that this will also restart the map in order to apply changes, assuming you didn't quit the engine first.
hit f8 while its stopped to change which statement gets executed next.
hit f9 to set a breakpoint. breakpoints set inside the gui will be re-applied on map restarts, and for all 3 modules.
hit f10 to single step (step over - treating child functions as if they were a single statement).
hit f11 to single step (step into - entering child functions).
hit shift+f11 to continue execution until it returns to the parent function (step out).
(these keyboard shortcuts should all be documented in fteqccgui's menus.)
you can mouse-over expressions to see their values, assuming execution is paused - otherwise it'll just try to display their types/descriptions.
or you can use the watch window thing to type in stuff that you want to re-evaluate each time the program stops.
(expression evaluation is fairly basic, and basically limited to globals and ent.field expressions, including basic assignments, locals are kinda messy. evaluating an ent will give a long list of its fields, integers on the lhs of an expression are interpreted as entity indexes, so '1.netname' will show the first player's name)
at the console:
watchpoint_[csqc|ssqc|menu] can be used to break your program when the watched expression changes (the memory address that stores the expression will be tested).
poke_[csqc|ssqc|menu] can be used to evaluate expressions.
a stack trace will be shown whenever the engine is waiting for the debugger to allow it to resume execution. you can read locals etc here.
while debugging:
any bounds checks, errors from builtins, etc, should trigger an exception. focus will be given to the gui (which will display the relevant error dialog), you can then use f8 to change where its executing, or change variables by evaluating some expression with an assignment in it (or just change the code and hit f7 to restart the thing).
use #pragma sourcefile "foo.src" in order to instruct fteqcc to compile additional modules. This will allow you to gracefully debug all three of ssqc+csqc+menuqc with a single debugging session. commonly you'll have a .src that just includes three such lines (if its called preprogs.src then it'll be favoured by default over progs.src).
not really sure if there's anything else worth mentioning.
fteqccgui debug protocol (for baker)
-qcdebug will be on the commandline if the engine is started via fteqccgui. If so, interpret the stdin/stdout as a debug session, otherwise as an alternative console. debug commands are all \n separated.
g2e debuggerwnd $hwnd - gui tells the engine which window to set focus to on exceptions (windows doesn't let programs grab focus from others, only for programs to voluntarily give focus to another).
g2e qcresume - tells the engine that it should start running now, and not stop until the next breakpoint or exception. silently ignored if not debugging.
g2e qcstep over - tells the engine that it should run until the line number changes (unless it hits a child function, in which case it runs until return as if it were a single instruction).
g2e qcstep into - tells the engine that it should run until the line number changes (first statement of a child function counts as a new line number).
g2e qcstep out - tells the engine that it should run until it returns to its current parent.
g2e qcbreakpoint $mode "$file" line - tells the engine to clear|set|toggle a breakpoint (mode is 0|1|2)
g2e qcjump "$file" line - tells the engine to change where its currently executing.
g2e qcinspect "$expression" "$scopename" - asks the engine to evaluate the given expression. Scopename is provided to allow the engine fail gracefully if the function isn't current or whatever. The result is returned via a qcvalue response.
g2e qcreloaded - lets the engine know that something has been recompiled, and that it should restart everything.
e2g status $status_text - engine tells the gui what its status is. This doesn't necessarily display anywhere, but is useful for the gui to know that its talking to an engine that knows whats going on. you might want to just use refocuswindow instead though, both remove any warning messages about the engine not supporting -qcdebug.
e2g qcstep "$file" $line - engine tells the gui that it has stopped, at the specified line.
e2g qcfault "$file" $line "reason" - engine tells the gui that it has stopped, at the specified line. the reason text will be messageboxed.
e2g qcvalue "$variableformula" "$value" - engine reports the value of a requested expression back to the gui
e2g qcreloaded "$vmname" "$progsname" - engine lets the gui know that it has loaded a 'new' progs, and is now waiting until told to qcresume (this allows the gui to resend breakpoints).
e2g refocuswindow $hwnd - engine reports its hwnd to the gui, so that the gui can give it focus any time execution is resumed.
so yeah, if the engine breaks then it needs to give focus to the gui and wait until qcstep/qcresume. qcreloaded should abort current execution and reload everything.
the engine will need to read the .lno files and figure out line numbers itself, filenames come from a statement<-functable->file lookup.
the engine will need its own expression evaluation logic, which can be as simple as global[.field[.field[.field]]], but future versions might expect more

If -qcdebug is used then Sys_Printf should not dump anything other than the above to the stdout, other stuff is kinda undefined. Be careful with input if you wish to respond to window events while broken, and limit any drawing to just the console or whatever. Remember to release any cursor grabs too, and that dedicated servers don't have any of that stuff. While you could potentially handle the qc* commands via the console, if the engine actually supports breaking then those commands will of course get lost, so these will HAVE to be handled early, and not just while broken. You'll get broken pipe errors if the gui quits before the engine - generally the engine should quit too, but whatever.
I'd be nice to switch this stuff to a named pipe, so that it can attach without needing to restart/select the engine or whatever, but mneh.
Me documenting this stuff means that Baker now has to cause it to propagate across the Earth, right? Good. Mwahaha...
.