mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2025-02-09 01:01:07 +00:00
Add support for string stats (for csqc's use).
This commit is contained in:
parent
402fedb147
commit
1a18a8af47
8 changed files with 104 additions and 25 deletions
|
@ -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);
|
||||
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.")},//
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue