• Register

Highly evolved engine based on id Software technology, available under dual license (GPL, proprietary licensing for commercial use available).

Post tutorial Report RSS Quake c - trace and report

Todays topic: using print code to hunt down bugs and logic faults.

Posted by on - Intermediate Server Side Coding

Sometimes you just dont know what your code is doing. Either through a logic
fault, or other agency. Especially with Quake C, thinks and touch functions
can operate in strange and unexpected ways.


Many development environments allow for a step / trace of running code.

This can be done with the darkplaces engine:
cvar prvm_traceqc is "0" ["0"] prints every QuakeC statement as it is executed (only for really thorough debugging!)

fteqcc even makes a "line number" file - progs.lno:
Black added support for loading LNO files produced by fteqcc (for reporting line numbers of QuakeC errors).

Unless you are seriously hardcore, like an assembler programmer, I'll just offer
the opinion this level of debug is over-kill for Quake C.

Mostly what I want is to know what a sequence of actions is or what
parameters a function call is working with.


1. bprint or dprint.

// in defs.qc
void(string s) bprint = #23;
void(string s) dprint = #25;

These both print the string "s" to the console. This can be logged in darkplaces:
cvar log_file is "" [""] filename to log messages to

dprint only prints if:
cvar developer is "0" ["0"] shows debugging messages and information (recommended for all developers and level designers); the value -1 also suppresses buffering and logging these messages

If you turn that on, you will find out the engine has a _lot_ of dprints!

Unstuck entity 202 (classname "") with offset 0.000000 0.000000 -8.000000.
Unstuck entity 238 (classname "") with offset 0.000000 0.000000 -7.000000.
Unstuck entity 218 (classname "") with offset 0.000000 0.000000 -7.000000.
Unstuck entity 229 (classname "") with offset 0.000000 0.000000 -8.000000.
SV_ModelIndex("progs/crate.bsp"): not precached (fix your code), precaching anyway
Stats for q1bsp model "progs/crate.bsp": 6 faces, 6 nodes, 7 leafs, 6 visleafs, 24 visleafportals, mesh: 24 vertices, 12 triangles, 6 surfaces
SV_ModelIndex("progs/pk_cube.bsp"): not precached (fix your code), precaching anyway
Stats for q1bsp model "progs/pk_cube.bsp": 66 faces, 41 nodes, 25 leafs, 24 visleafs, 148 visleafportals, mesh: 288 vertices, 156 triangles, 66 surfaces
Del: __spawn_16_delay is not defined
Del: __spawn_16_think is not defined
Del: __spawn_16_nextthink is not defined
Del: __spawn_16_count is not defined
Del: __spawn_16_target is not defined
SV_ModelIndex("progs/null.mdl"): not precached (fix your code), precaching anyway
SV_ModelIndex("progs/vw_lasercannon.mdl"): not precached (fix your code), precaching anyway
VM_entityfieldname: server: field index out of bounds

Once you start putting in your own warnings (and forgetting to take them out)
you get a lot of your own console spam.

If you use fteqcc you can use # directives:

#ifdef warning
			bprint(" -- code used: ");
			bprint(ftos(e.mcode));
			bprint(" - slot: ");
			bprint(ftos(e.weapon));
			bprint("\n");
#endifdef

You still might want a management system for complex code:

// defs.qc
#define warning

#ifdef warning
// bit mask selection
float WARNING;
// text comparison selection
string warnings;
#endifdef

// in some function
#ifdef warning
		if (WARNING & 1)
		{
			bprint(" --- code for warning 1\n");
		}
#endifdef

// in another function
#ifdef warning
		if (WARNING & 2)
		{
			bprint(" --- code for warning 2\n");
		}
#endifdef

// more code
#ifdef warning
		if (warnigns == "this")
		{
			bprint(" --- code for 'this' warning\n");
		}
#endifdef

// yet more code
#ifdef warning
		if (warnigns == "that")
		{
			bprint(" --- code for 'that' warning\n");
		}
#endifdef

Bit masks are a good way to select various pieces of warning code.
You can set warning to 3 for printing both warnings:
prvm_globalset server warning 3

The string "warnings" makes it easy to remember a single warning instance.
prvm_globalset server warnings this

# directives also let you be lazy removing warning code.
For beta releases just comment out:
//#define warning

More properly, you can also use the "#ifdef warning" as a guide
to strip warning code from the final release.

A flexible system that will serve most of your trace and report needs.
Yes, I stated "most".

You will find bprint does not always make it to the console.
dprint most likely does - I usually dont use it because of the engine spam.

worldspawn() is one place you cant use bprint.


There are 2 more options:

2. a think entity

#ifdef warning
// earlier
void() warn_print =
{
	bprint(self.noise);
	bprint(self.noise1);
	bprint(self.noise2);
	bprint(self.noise3);
	bprint(self.noise4);
	bprint("\n");
	remove(self);
}
#endifdef

// in some function
#ifdef warning

newmis = spawn(); // use another global ent pointer if tracing missle launch code
newmis.think = warn_print;
newmis.nextthink = time + 3;
newmis.noise =" -- code used: ";
newmis.noise1 =ftos(e.mcode);
newmis.noise2 =" - slot: ";
newmis.noise3 =ftos(e.weapon);

#endifdef

3. server console variables (a darkplaces option)

// defs.qc
float system_hash; // track various things
#define syh  system_hash = system_hash + 1 + rint(random() * 2);

dpext.qc:float(string name, string value) registercvar = #93;
dpext.qc:string(string s1, string s2, ...) strcat = #115; // concatenates two or more strings (for example "abc", "def" would return "abcdef") and returns as a tempstring

// some sound code
#ifdef warning
		syh
		registercvar(strcat("___snd_",ftos(system_hash)),strcat(samp," org: ",vtos(e.origin)," e: ",ftos(num_for_edict(e))));

#endifdef

// in the console type
___snd[TAB]

___snd_10022 is "misc/null.wav org: '  0.0   0.0   0.0' e: 1" ["misc/null.wav org: '  0.0   0.0   0.0' e: 1"] custom cvar
___snd_10023 is "misc/null.wav org: '  0.0   0.0   0.0' e: 1" ["misc/null.wav org: '  0.0   0.0   0.0' e: 1"] custom cvar
___snd_10025 is "misc/teleport/tele_4.wav org: '113.9 -382.1 -135.0' e: 152" ["misc/teleport/tele_4.wav org: '113.9 -382.1 -135.0' e: 152"] custom cvar

I like this method for the self sorting nature of the data.
Then it can be recalled when you want to look at it.

I may post a follow up of a function call entry trace I've been thinking of
implementing. I came up with an easy way to try while writing this.

Good luck with Quake C and keep the code flowing!

Post a comment

Your comment will be anonymous unless you join the community. Or sign in with your social account: