Add support for string stats (for csqc's use).

This commit is contained in:
Shpoike 2020-06-19 23:19:07 +01:00
parent 402fedb147
commit 1a18a8af47
8 changed files with 104 additions and 25 deletions

View file

@ -88,6 +88,17 @@ void CL_ClearTrailStates(void)
}
}
void CL_FreeState(void)
{
int i;
for (i = 0; i < MAX_CL_STATS; i++)
free(cl.statss[i]);
CL_ClearTrailStates();
PR_ClearProgs(&cl.qcvm);
free(cl.static_entities);
memset (&cl, 0, sizeof(cl));
}
/*
=====================
CL_ClearState
@ -99,12 +110,8 @@ void CL_ClearState (void)
if (!sv.active)
Host_ClearMemory ();
CL_ClearTrailStates();
PR_ClearProgs(&cl.qcvm);
// wipe the entire cl structure
memset (&cl, 0, sizeof(cl));
CL_FreeState();
SZ_Clear (&cls.message);

View file

@ -2154,6 +2154,17 @@ static void CL_ParseStatInt(int stat, int ival)
{
CL_ParseStatNumeric(stat,ival,ival);
}
static void CL_ParseStatString(int stat, const char *str)
{
if (stat < 0 || stat >= MAX_CL_STATS)
{
Con_DWarning ("svc_updatestat: %i is invalid\n", stat);
return;
}
free(cl.statss[stat]);
cl.statss[stat] = strdup(str);
//hud doesn't know/care about any of these strings so don't bother invalidating anything.
}
//mods and servers might not send the \n instantly.
//some mods bug out and omit the \n entirely, this function helps prevent the damage from spreading too much.
@ -2651,10 +2662,7 @@ void CL_ParseServerMessage (void)
if (!(cl.protocol_pext2 & PEXT2_REPLACEMENTDELTAS))
Host_Error ("Received svcfte_updatestatstring but extension not active");
i = MSG_ReadByte ();
if (i >= 0 && i < MAX_CL_STATS)
/*cl.statss[i] =*/ MSG_ReadString ();
else
Con_Warning ("svcfte_updatestatstring: %i is invalid\n", i);
CL_ParseStatString(i, MSG_ReadString());
break;
case svcfte_updatestatfloat:
if (!(cl.protocol_pext2 & PEXT2_REPLACEMENTDELTAS))

View file

@ -164,6 +164,7 @@ typedef struct
// information for local display
int stats[MAX_CL_STATS]; // health, etc
float statsf[MAX_CL_STATS];
char *statss[MAX_CL_STATS];
int items; // inventory bit flags
float item_gettime[32]; // cl.time of aquiring item, for blinking
float faceanimtime; // use anim frame if cl.time < this
@ -394,6 +395,7 @@ void CL_ParseEffect (qboolean big);
void CL_ParseTEnt (void);
void CL_UpdateTEnts (void);
void CL_FreeState(void);
void CL_ClearState (void);
void CL_ClearTrailStates(void);

View file

@ -3316,7 +3316,7 @@ void Mod_LoadAliasModel (qmodel_t *mod, void *buffer, int pvtype)
for (i=0 ; i<pheader->numverts ; i++)
{
stverts[i].onseam = LittleLong (pinstverts[i].onseam);
stverts[i].onseam = LittleLong (pinstverts[i].onseam); //should only be 0 or ALIAS_ONSEAM. other values (particuarly 1) is a model bug and will be treated as ALIAS_ONSEAM in this implementation.
stverts[i].s = LittleLong (pinstverts[i].s);
stverts[i].t = LittleLong (pinstverts[i].t);
}

View file

@ -589,12 +589,11 @@ void Host_ClearMemory (void)
Hunk_FreeToLowMark (host_hunklevel);
cls.signon = 0;
PR_ClearProgs(&sv.qcvm);
PR_ClearProgs(&cl.qcvm);
free(cl.static_entities);
free(sv.static_entities); //spike -- this is dynamic too, now
free(sv.ambientsounds);
memset (&sv, 0, sizeof(sv));
memset (&cl, 0, sizeof(cl));
CL_FreeState();
}
@ -818,7 +817,7 @@ void _Host_Frame (double time)
CL_AccumulateCmd ();
//Run the server+networking (client->server->client), at a different rate from everyt
//Run the server+networking (client->server->client), at a different rate from everything else
if (accumtime >= host_netinterval)
{
float realframetime = host_frametime;

View file

@ -4529,6 +4529,18 @@ static void PF_cl_getstat_float(void)
else
G_FLOAT(OFS_RETURN) = cl.statsf[stnum];
}
static void PF_cl_getstat_string(void)
{
int stnum = G_FLOAT(OFS_PARM0);
if (stnum < 0 || stnum > countof(cl.statss) || !cl.statss[stnum])
G_INT(OFS_RETURN) = 0;
else
{
char *result = PR_GetTempString();
q_strlcpy(result, cl.statss[stnum], STRINGTEMP_LENGTH);
G_INT(OFS_RETURN) = PR_SetEngineString(result);
}
}
struct
{
@ -5450,7 +5462,7 @@ static struct
// {"drawrotsubpic", PF_NoSSQC, PF_FullCSQCOnly, 0, D("void(vector pivot, vector mins, vector maxs, string pic, vector txmin, vector txsize, vector rgb, vector alphaandangles)", "Overcomplicated draw function for over complicated people. Positions follow drawrotpic, while texture coords follow drawsubpic. Due to argument count limitations in builtins, the alpha value and angles are combined into separate fields of a vector (tip: use fteqcc's [alpha, angle] feature.")},
{"getstati", PF_NoSSQC, PF_cl_getstat_int, 330, D("#define getstati_punf(stnum) (float)(__variant)getstati(stnum)\nint(float stnum)", "Retrieves the numerical value of the given EV_INTEGER or EV_ENTITY stat. Use getstati_punf if you wish to type-pun a float stat as an int to avoid truncation issues in DP.")},// (EXT_CSQC)
{"getstatf", PF_NoSSQC, PF_cl_getstat_float,331, D("#define getstatbits getstatf\nfloat(float stnum, optional float firstbit, optional float bitcount)", "Retrieves the numerical value of the given EV_FLOAT stat. If firstbit and bitcount are specified, retrieves the upper bits of the STAT_ITEMS stat (converted into a float, so there are no VM dependancies).")},// (EXT_CSQC)
// {"getstats", PF_NoSSQC, PF_cl_getstat_str, 332, D("string(float stnum)", "Retrieves the value of the given EV_STRING stat, as a tempstring.\nOlder engines may use 4 consecutive integer stats, with a limit of 15 chars (yes, really. 15.), but "FULLENGINENAME" uses a separate namespace for string stats and has a much higher length limit.")},
{"getstats", PF_NoSSQC, PF_cl_getstat_string,332, D("string(float stnum)", "Retrieves the value of the given EV_STRING stat, as a tempstring.\nString stats use a separate pool of stats from numeric ones.\n")},
// {"getplayerstat", PF_NoSSQC, PF_FullCSQCOnly, 0, D("__variant(float playernum, float statnum, float stattype)", "Retrieves a specific player's stat, matching the type specified on the server. This builtin is primarily intended for mvd playback where ALL players are known. For EV_ENTITY, world will be returned if the entity is not in the pvs, use type-punning with EV_INTEGER to get the entity number if you just want to see if its set. STAT_ITEMS should be queried as an EV_INTEGER on account of runes and items2 being packed into the upper bits.")},
{"setmodelindex", PF_sv_setmodelindex,PF_cl_setmodelindex,333, D("void(entity e, float mdlindex)", "Sets a model by precache index instead of by name. Otherwise identical to setmodel.")},//
// {"modelnameforindex",PF_modelnameforidx,PF_modelnameforidx, 334, D("string(float mdlindex)", "Retrieves the name of the model based upon a precache index. This can be used to reduce csqc network traffic by enabling model matching.")},//

View file

@ -147,9 +147,11 @@ typedef struct client_s
qboolean pextknown;
unsigned int protocol_pext2;
unsigned int resendstats[MAX_CL_STATS/32]; //the stats which need to be resent.
unsigned int resendstatsnum[MAX_CL_STATS/32]; //the stats which need to be resent.
unsigned int resendstatsstr[MAX_CL_STATS/32]; //the stats which need to be resent.
int oldstats_i[MAX_CL_STATS]; //previous values of stats. if these differ from the current values, reflag resendstats.
float oldstats_f[MAX_CL_STATS]; //previous values of stats. if these differ from the current values, reflag resendstats.
char *oldstats_s[MAX_CL_STATS];
struct entity_num_state_s{
unsigned int num; //ascending order, there can be gaps.
entity_state_t state;
@ -167,7 +169,8 @@ typedef struct client_s
//reflagged state includes stats updates, entity updates, and entity removes.
int sequence; //to see if its stale
float timestamp;
unsigned int resendstats[MAX_CL_STATS/32];
unsigned int resendstatsnum[MAX_CL_STATS/32];
unsigned int resendstatsstr[MAX_CL_STATS/32];
struct
{
unsigned int num;

View file

@ -34,7 +34,7 @@ unsigned int sv_protocol_pext2 = PEXT2_SUPPORTED_SERVER; //spike
//============================================================================
void SV_CalcStats(client_t *client, int *statsi, float *statsf)
void SV_CalcStats(client_t *client, int *statsi, float *statsf, const char **statss)
{
size_t i;
edict_t *ent = client->edict;
@ -48,6 +48,7 @@ void SV_CalcStats(client_t *client, int *statsi, float *statsf)
memset(statsi, 0, sizeof(*statsi)*MAX_CL_STATS);
memset(statsf, 0, sizeof(*statsf)*MAX_CL_STATS);
memset(statss, 0, sizeof(*statss)*MAX_CL_STATS);
statsf[STAT_HEALTH] = ent->v.health;
// statsf[STAT_FRAGS] = ent->v.frags; //obsolete
statsi[STAT_WEAPON] = SV_ModelIndex(PR_GetString(ent->v.weaponmodel));
@ -133,6 +134,8 @@ void SV_CalcStats(client_t *client, int *statsi, float *statsf)
statsf[sv.customstats[i].idx+2] = eval->vector[2];
break;
case ev_string: //not supported in this build... send with svcfte_updatestatstring on change, which is annoying.
statss[sv.customstats[i].idx] = PR_GetString(eval->string);
break;
case ev_void: //nothing...
case ev_field: //panic! everyone panic!
case ev_function: //doesn't make much sense
@ -579,6 +582,14 @@ static size_t snapshot_maxents;
void SVFTE_DestroyFrames(client_t *client)
{
int i;
for (i = 0; i < MAX_CL_STATS; i++)
{
if (!client->oldstats_s[i])
continue;
free(client->oldstats_s[i]);
client->oldstats_s[i] = 0;
}
if (client->previousentities)
free(client->previousentities);
client->previousentities = NULL;
@ -639,7 +650,10 @@ static void SVFTE_DroppedFrame(client_t *client, int sequence)
frame->sequence = -1;
//flag their stats for resend
for (i = 0; i < MAX_CL_STATS/32; i++)
client->resendstats[i] |= frame->resendstats[i];
{
client->resendstatsnum[i] |= frame->resendstatsnum[i];
client->resendstatsstr[i] |= frame->resendstatsstr[i];
}
//flag their various entities as needing a resend too.
for (i = 0; i < frame->numents; i++)
client->pendingentities_bits[frame->ents[i].num] |= frame->ents[i].bits;
@ -676,9 +690,16 @@ static void SVFTE_WriteStats(client_t *client, sizebuf_t *msg)
{
int statsi[MAX_CL_STATS];
float statsf[MAX_CL_STATS];
const char *statss[MAX_CL_STATS];
int i;
struct deltaframe_s *frame;
int sequence = NET_QSocketGetSequenceOut(client->netconnection);
int maxstats;
if (client->protocol_pext2 & PEXT2_REPLACEMENTDELTAS)
maxstats = MAX_CL_STATS;
else
maxstats = 32;
frame = &client->frames[sequence&(client->numframes-1)];
@ -686,9 +707,9 @@ static void SVFTE_WriteStats(client_t *client, sizebuf_t *msg)
SVFTE_DroppedFrame(client, frame->sequence);
//figure out the current values in a nice easy way (yay for copying to make arrays easier!)
SV_CalcStats(client, statsi, statsf);
SV_CalcStats(client, statsi, statsf, statss);
for (i = 0; i < MAX_CL_STATS; i++)
for (i = 0; i < maxstats; i++)
{
//small cleanup
if (!statsi[i])
@ -701,14 +722,28 @@ static void SVFTE_WriteStats(client_t *client, sizebuf_t *msg)
{
client->oldstats_i[i] = statsi[i];
client->oldstats_f[i] = statsf[i];
client->resendstats[i/32] |= 1u<<(i&31);
client->resendstatsnum[i/32] |= 1u<<(i&31);
}
if (statss[i] || client->oldstats_s[i])
{
const char *os = client->oldstats_s[i];
const char *ns = statss[i];
if (!ns) ns="";
if (!os) os="";
if (strcmp(os,ns))
{
client->resendstatsstr[i/32] |= 1u<<(i&31);
free(client->oldstats_s[i]);
client->oldstats_s[i] = strdup(ns);
}
}
//if its flagged then unflag it, log it, and send it
if (client->resendstats[i/32] & (1u<<(i&31)))
if (client->resendstatsnum[i/32] & (1u<<(i&31)))
{
client->resendstats[i/32] &= ~(1u<<(i&31));
frame->resendstats[i/32] |= 1u<<(i&31);
client->resendstatsnum[i/32] &= ~(1u<<(i&31));
frame->resendstatsnum[i/32] |= 1u<<(i&31);
if ((double)statsi[i] != statsf[i] && statsf[i])
{ //didn't round nicely, so send as a float
@ -732,6 +767,19 @@ static void SVFTE_WriteStats(client_t *client, sizebuf_t *msg)
}
}
}
//if its flagged then unflag it, log it, and send it
if (client->resendstatsstr[i/32] & (1u<<(i&31)))
{
client->resendstatsstr[i/32] &= ~(1u<<(i&31));
frame->resendstatsstr[i/32] |= 1u<<(i&31);
MSG_WriteByte(msg, svcfte_updatestatstring);
MSG_WriteByte(msg, i);
if (statss[i])
MSG_WriteString(msg, statss[i]);
else
MSG_WriteString(msg, NULL);
}
}
}
static void SVFTE_CalcEntityDeltas(client_t *client)