Skip to content

Commit

Permalink
Pretty-print a few known entity fields (from Ironwail)
Browse files Browse the repository at this point in the history
`edict`/`edicts` console commands and `r_showfields 1`
will now pretty-print `movetype`, `solid`, `deadflag`,
`takedamage`, `flags`, `spawnflags`, `effects` and
`nextthink` entity fields.

Also tweaked float/vector printing format.
  • Loading branch information
andrei-drexler authored and vsonnier committed Jul 12, 2024
1 parent 82ff437 commit 03fee44
Show file tree
Hide file tree
Showing 2 changed files with 264 additions and 26 deletions.
289 changes: 263 additions & 26 deletions Quake/pr_edict.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ int type_size[8] = {
1 // sizeof(void *) / 4 // ev_pointer
};

#define NUM_TYPE_SIZES (int)countof (type_size)

static ddef_t *ED_FieldAtOfs (int ofs);

cvar_t nomonsters = {"nomonsters", "0", CVAR_NONE};
Expand Down Expand Up @@ -242,6 +244,36 @@ eval_t *GetEdictFieldValue (edict_t *ed, int fldofs)
return (eval_t *)((char *)&ed->v + fldofs * 4);
}

/*
============
GetEdictFieldValueByName
============
*/
eval_t *GetEdictFieldValueByName (edict_t *ed, const char *name)
{
return GetEdictFieldValue (ed, ED_FindFieldOffset (name));
}

/*
============
PR_FloatFormat
============
*/
static const char *PR_FloatFormat (float f)
{
return fabs (f - round (f)) < 0.05f ? "% 5.0f " : "% 7.1f";
}

/*
============
PR_DoubleFormat
============
*/
static const char *PR_DoubleFormat (double d)
{
return abs (d - round (d)) < 0.05 ? "% 13.0f " : "% 15.1f";
}

/*
============
PR_ValueString
Expand All @@ -253,6 +285,7 @@ Returns a string describing *data in a type specific manner
static const char *PR_ValueString (int type, eval_t *val)
{
static char line[512];
char fmt[64];
ddef_t *def;
dfunction_t *f;

Expand All @@ -278,10 +311,14 @@ static const char *PR_ValueString (int type, eval_t *val)
q_snprintf (line, sizeof (line), "void");
break;
case ev_float:
q_snprintf (line, sizeof (line), "%5.1f", val->_float);
// Note: leading space, so that float fields are aligned with the first value in vector fields
q_snprintf (fmt, sizeof (fmt), " %s", PR_FloatFormat (val->_float));
q_snprintf (line, sizeof (line), fmt, val->_float);
break;
case ev_ext_double:
q_snprintf (line, sizeof (line), "%5.1f", val->_double);
// Note: leading space, so that double fields are aligned with the first value in vector fields
q_snprintf (fmt, sizeof (fmt), " %s", PR_DoubleFormat (val->_double));
q_snprintf (line, sizeof (line), fmt, val->_double);
break;
case ev_ext_integer:
q_snprintf (line, sizeof (line), "%i", val->_int);
Expand All @@ -296,7 +333,8 @@ static const char *PR_ValueString (int type, eval_t *val)
sprintf (line, "%" PRIu64, val->_uint64);
break;
case ev_vector:
q_snprintf (line, sizeof (line), "'%5.1f %5.1f %5.1f'", val->vector[0], val->vector[1], val->vector[2]);
q_snprintf (fmt, sizeof (fmt), "'%s %s %s'", PR_FloatFormat (val->vector[0]), PR_FloatFormat (val->vector[1]), PR_FloatFormat (val->vector[2]));
q_snprintf (line, sizeof (line), fmt, val->vector[0], val->vector[1], val->vector[2]);
break;
case ev_pointer:
q_snprintf (line, sizeof (line), "pointer");
Expand Down Expand Up @@ -438,6 +476,213 @@ const char *PR_GlobalStringNoContents (int ofs)
return line;
}

/*
=============
ED_IsRelevantField
Returns true if the field should be printed by the edict command:
- not a _x/_y_z variable
- non-zero contents
=============
*/
static qboolean ED_IsRelevantField (edict_t *ed, ddef_t *d)
{
const char *name;
size_t l;
int *v;
int type;
int i;

name = PR_GetString (d->s_name);
l = strlen (name);
if (l > 1 && name[l - 2] == '_')
return false; // skip _x, _y, _z vars

type = d->type & ~DEF_SAVEGLOBAL;
if (type >= NUM_TYPE_SIZES)
return false;

// if the value is still all 0, skip the field
v = (int *)((char *)&ed->v + d->ofs * 4);
for (i = 0; i < type_size[type]; i++)
if (v[i])
return true;

return false;
}

/*
=============
ED_AppendFlagString
=============
*/
static void ED_AppendFlagString (char *dst, size_t dstsize, const char *desc)
{
if (*dst)
q_strlcat (dst, " | ", dstsize);
q_strlcat (dst, desc, dstsize);
}

/*
=============
ED_FieldValueString
=============
*/
static const char *ED_FieldValueString (edict_t *ed, ddef_t *d)
{
static char str[1024];
int ofs = d->ofs * 4;
eval_t *val = (eval_t *)((char *)&ed->v + ofs);

// .movetype
if (ofs == offsetof (entvars_t, movetype) && val->_float == (int)val->_float)
{
switch ((int)val->_float)
{
#define MOVETYPE_CASE(x) \
case x: \
return #x
MOVETYPE_CASE (MOVETYPE_NONE);
MOVETYPE_CASE (MOVETYPE_ANGLENOCLIP);
MOVETYPE_CASE (MOVETYPE_ANGLECLIP);
MOVETYPE_CASE (MOVETYPE_WALK);
MOVETYPE_CASE (MOVETYPE_STEP);
MOVETYPE_CASE (MOVETYPE_FLY);
MOVETYPE_CASE (MOVETYPE_TOSS);
MOVETYPE_CASE (MOVETYPE_PUSH);
MOVETYPE_CASE (MOVETYPE_NOCLIP);
MOVETYPE_CASE (MOVETYPE_FLYMISSILE);
MOVETYPE_CASE (MOVETYPE_BOUNCE);
MOVETYPE_CASE (MOVETYPE_GIB);
#undef MOVETYPE_CASE
default:
break;
}
}

// .solid
if (ofs == offsetof (entvars_t, solid) && val->_float == (int)val->_float)
{
switch ((int)val->_float)
{
#define SOLID_CASE(x) \
case x: \
return #x
SOLID_CASE (SOLID_NOT);
SOLID_CASE (SOLID_TRIGGER);
SOLID_CASE (SOLID_BBOX);
SOLID_CASE (SOLID_SLIDEBOX);
SOLID_CASE (SOLID_BSP);
#undef SOLID_CASE
default:
break;
}
}

// .deadflag
if (ofs == offsetof (entvars_t, deadflag) && val->_float == (int)val->_float)
{
switch ((int)val->_float)
{
#define DEAD_CASE(x) \
case x: \
return #x
DEAD_CASE (DEAD_NO);
DEAD_CASE (DEAD_DYING);
DEAD_CASE (DEAD_DEAD);
DEAD_CASE (DEAD_RESPAWNABLE);
#undef DEAD_CASE
default:
break;
}
}

// .takedamage
if (ofs == offsetof (entvars_t, takedamage) && val->_float == (int)val->_float)
{
switch ((int)val->_float)
{
#define TAKEDAMAGE_CASE(x) \
case x: \
return #x
TAKEDAMAGE_CASE (DAMAGE_NO);
TAKEDAMAGE_CASE (DAMAGE_YES);
TAKEDAMAGE_CASE (DAMAGE_AIM);
#undef TAKEDAMAGE_CASE
default:
break;
}
}

// bitfield: .flags, .spawnflags, .effects
if ((ofs == offsetof (entvars_t, flags) || ofs == offsetof (entvars_t, spawnflags) || ofs == offsetof (entvars_t, effects)) &&
val->_float == (int)val->_float)
{
int bits = (int)val->_float;
str[0] = '\0';

#define BIT_CASE(f) \
do \
{ \
if (bits & (int)f) \
{ \
bits ^= (int)f; \
ED_AppendFlagString (str, sizeof (str), #f); \
} \
} while (0)

if (ofs == offsetof (entvars_t, flags))
{
BIT_CASE (FL_FLY);
BIT_CASE (FL_CONVEYOR);
BIT_CASE (FL_CLIENT);
BIT_CASE (FL_INWATER);
BIT_CASE (FL_MONSTER);
BIT_CASE (FL_GODMODE);
BIT_CASE (FL_NOTARGET);
BIT_CASE (FL_ITEM);
BIT_CASE (FL_ONGROUND);
BIT_CASE (FL_PARTIALGROUND);
BIT_CASE (FL_WATERJUMP);
BIT_CASE (FL_JUMPRELEASED);
}
else if (ofs == offsetof (entvars_t, spawnflags))
{
BIT_CASE (SPAWNFLAG_NOT_EASY);
BIT_CASE (SPAWNFLAG_NOT_MEDIUM);
BIT_CASE (SPAWNFLAG_NOT_HARD);
BIT_CASE (SPAWNFLAG_NOT_DEATHMATCH);
}
else if (ofs == offsetof (entvars_t, effects))
{
BIT_CASE (EF_BRIGHTFIELD);
BIT_CASE (EF_MUZZLEFLASH);
BIT_CASE (EF_BRIGHTLIGHT);
BIT_CASE (EF_DIMLIGHT);
}

#undef BIT_CASE

while (bits)
{
int lowest = bits & -bits;
bits ^= lowest;
ED_AppendFlagString (str, sizeof (str), va ("%d", lowest));
}

return str;
}

// .nextthink
if (ofs == offsetof (entvars_t, nextthink) && val->_float)
{
return va (" %7.1f (%+.2f)", val->_float, val->_float - qcvm->time);
}

// generic field
return PR_ValueString (d->type, val);
}

/*
=============
ED_Print
Expand All @@ -447,45 +692,37 @@ For debugging
*/
void ED_Print (edict_t *ed)
{
ddef_t *d;
int *v;
int i, j, l;
const char *name;
int type;
ddef_t *d;
int i, l;
char field[4096], buf[4096], *p;

if (ed->free)
{
Con_SafePrintf ("FREE\n");
return;
}

Con_SafePrintf ("\nEDICT %i:\n", NUM_FOR_EDICT (ed)); // johnfitz -- was Con_Printf
q_snprintf (buf, sizeof (buf), "\nEDICT %i:\n", NUM_FOR_EDICT (ed)); // johnfitz -- was Con_Printf
p = buf + strlen (buf);
for (i = 1; i < qcvm->progs->numfielddefs; i++)
{
d = &qcvm->fielddefs[i];
name = PR_GetString (d->s_name);
l = strlen (name);
if (l > 1 && name[l - 2] == '_')
continue; // skip _x, _y, _z vars

v = (int *)((char *)&ed->v + d->ofs * 4);

// if the value is still all 0, skip the field
type = d->type & ~DEF_SAVEGLOBAL;

if (type >= countof (type_size))
if (!ED_IsRelevantField (ed, d))
continue;

for (j = 0; j < type_size[type]; j++)
q_snprintf (field, sizeof (field), "%-14s %s\n", PR_GetString (d->s_name), ED_FieldValueString (ed, d)); // johnfitz -- was Con_Printf
l = strlen (field);
if (l + 1 > buf + sizeof (buf) - p)
{
if (v[j])
break;
Con_SafePrintf ("%s", buf);
p = buf;
}
if (j == type_size[type])
continue;

Con_SafePrintf ("%-14s %s\n", name, PR_ValueString (d->type, (eval_t *)v)); // johnfitz -- was Con_Printf
memcpy (p, field, l + 1);
p += l;
}

Con_SafePrintf ("%s", buf);
}

/*
Expand Down
1 change: 1 addition & 0 deletions Quake/server.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ typedef enum
DEAD_NO = 0,
DEAD_DYING = 1,
DEAD_DEAD = 2,
DEAD_RESPAWNABLE = 3,
} edeadflag_t;

// edict->takedamage
Expand Down

0 comments on commit 03fee44

Please sign in to comment.