mirror of
https://github.com/Shpoike/Quakespasm.git
synced 2025-03-11 12:30:55 +00:00
Finally give the SSQC the ability to send entity updates to the CSQC itself (as well as detect when a client is running CSQC).
This commit is contained in:
parent
227db32137
commit
e8cd3bd846
5 changed files with 234 additions and 16 deletions
|
@ -2586,6 +2586,25 @@ static void Host_Download_f(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void Host_EnableCSQC_f(void)
|
||||||
|
{
|
||||||
|
size_t e;
|
||||||
|
if (cmd_source != src_client)
|
||||||
|
return;
|
||||||
|
host_client->csqcactive = true;
|
||||||
|
|
||||||
|
//re-flag any ents as needing a resend.
|
||||||
|
for (e = 1; e < host_client->numpendingcsqcentities; e++)
|
||||||
|
if (host_client->pendingcsqcentities_bits[e] & SENDFLAG_PRESENT)
|
||||||
|
host_client->pendingcsqcentities_bits[e] |= SENDFLAG_USABLE;
|
||||||
|
}
|
||||||
|
static void Host_DisableCSQC_f(void)
|
||||||
|
{
|
||||||
|
if (cmd_source != src_client)
|
||||||
|
return;
|
||||||
|
host_client->csqcactive = false;
|
||||||
|
}
|
||||||
|
|
||||||
static void Host_StartDownload_f(void)
|
static void Host_StartDownload_f(void)
|
||||||
{
|
{
|
||||||
if (cmd_source != src_client)
|
if (cmd_source != src_client)
|
||||||
|
@ -2843,6 +2862,8 @@ void Host_InitCommands (void)
|
||||||
Cmd_AddCommand_ClientCommand ("give", Host_Give_f);
|
Cmd_AddCommand_ClientCommand ("give", Host_Give_f);
|
||||||
Cmd_AddCommand_ClientCommand ("download", Host_Download_f);
|
Cmd_AddCommand_ClientCommand ("download", Host_Download_f);
|
||||||
Cmd_AddCommand_ClientCommand ("sv_startdownload", Host_StartDownload_f);
|
Cmd_AddCommand_ClientCommand ("sv_startdownload", Host_StartDownload_f);
|
||||||
|
Cmd_AddCommand_ClientCommand ("enablecsqc", Host_EnableCSQC_f);
|
||||||
|
Cmd_AddCommand_ClientCommand ("disablecsqc", Host_DisableCSQC_f);
|
||||||
|
|
||||||
Cmd_AddCommand ("startdemos", Host_Startdemos_f);
|
Cmd_AddCommand ("startdemos", Host_Startdemos_f);
|
||||||
Cmd_AddCommand ("demos", Host_Demos_f);
|
Cmd_AddCommand ("demos", Host_Demos_f);
|
||||||
|
|
|
@ -1591,6 +1591,7 @@ sizebuf_t *WriteDest (void)
|
||||||
return &sv.signon;
|
return &sv.signon;
|
||||||
|
|
||||||
case MSG_EXT_MULTICAST:
|
case MSG_EXT_MULTICAST:
|
||||||
|
case MSG_EXT_ENTITY: //just reuse it...
|
||||||
return &sv.multicast;
|
return &sv.multicast;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -4138,7 +4138,7 @@ static void PF_infokey_internal(qboolean returnfloat)
|
||||||
else if (!strcmp(key, "*spectator"))
|
else if (!strcmp(key, "*spectator"))
|
||||||
r = "";
|
r = "";
|
||||||
else if (!strcmp(key, "csqcactive"))
|
else if (!strcmp(key, "csqcactive"))
|
||||||
r = "";
|
r = (cl->csqcactive?"1":"0");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
r = Info_GetKey(cl->userinfo, key, buf, sizeof(buf));
|
r = Info_GetKey(cl->userinfo, key, buf, sizeof(buf));
|
||||||
|
|
|
@ -164,6 +164,11 @@ typedef struct client_s
|
||||||
unsigned int snapshotresume;
|
unsigned int snapshotresume;
|
||||||
unsigned int *pendingentities_bits; //UF_ flags for each entity
|
unsigned int *pendingentities_bits; //UF_ flags for each entity
|
||||||
size_t numpendingentities; //realloc if too small
|
size_t numpendingentities; //realloc if too small
|
||||||
|
unsigned int *pendingcsqcentities_bits; //SendFlags bitflags for each entity
|
||||||
|
#define SENDFLAG_PRESENT 0x80000000u //tracks that we previously sent one of these ents (resulting in a remove if the ent gets remove()d).
|
||||||
|
#define SENDFLAG_REMOVE 0x40000000u //for packetloss to signal that we need to resend a remove.
|
||||||
|
#define SENDFLAG_USABLE 0x00ffffffu //SendFlags bits that the qc is actually able to use (don't get confused if the mod uses SendFlags=-1).
|
||||||
|
size_t numpendingcsqcentities; //realloc if too small
|
||||||
struct deltaframe_s
|
struct deltaframe_s
|
||||||
{ //quick overview of how this stuff actually works:
|
{ //quick overview of how this stuff actually works:
|
||||||
//when the server notices a gap in the ack sequence, we walk through the dropped frames and reflag everything that was dropped.
|
//when the server notices a gap in the ack sequence, we walk through the dropped frames and reflag everything that was dropped.
|
||||||
|
@ -178,6 +183,7 @@ typedef struct client_s
|
||||||
{
|
{
|
||||||
unsigned int num;
|
unsigned int num;
|
||||||
unsigned int bits;
|
unsigned int bits;
|
||||||
|
unsigned int csqcbits;
|
||||||
} *ents;
|
} *ents;
|
||||||
int numents; //doesn't contain an entry for every entity, just ones that were sent this frame. no 0 bits
|
int numents; //doesn't contain an entry for every entity, just ones that were sent this frame. no 0 bits
|
||||||
int maxents;
|
int maxents;
|
||||||
|
@ -201,6 +207,7 @@ typedef struct client_s
|
||||||
//for more speed, the server should build a collection of blocks to track which parts were actually acked, thereby avoiding redundant resends, but in the intererest of simplicity...
|
//for more speed, the server should build a collection of blocks to track which parts were actually acked, thereby avoiding redundant resends, but in the intererest of simplicity...
|
||||||
} download;
|
} download;
|
||||||
qboolean knowntoqc; // putclientinserver was called
|
qboolean knowntoqc; // putclientinserver was called
|
||||||
|
qboolean csqcactive; // its prepared to accept csqc entities.
|
||||||
} client_t;
|
} client_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
215
Quake/sv_main.c
215
Quake/sv_main.c
|
@ -603,6 +603,11 @@ void SVFTE_DestroyFrames(client_t *client)
|
||||||
client->pendingentities_bits = NULL;
|
client->pendingentities_bits = NULL;
|
||||||
client->numpendingentities = 0;
|
client->numpendingentities = 0;
|
||||||
|
|
||||||
|
if (client->pendingcsqcentities_bits)
|
||||||
|
free(client->pendingcsqcentities_bits);
|
||||||
|
client->pendingcsqcentities_bits = NULL;
|
||||||
|
client->numpendingcsqcentities = 0;
|
||||||
|
|
||||||
while(client->numframes > 0)
|
while(client->numframes > 0)
|
||||||
{
|
{
|
||||||
client->numframes--;
|
client->numframes--;
|
||||||
|
@ -641,6 +646,10 @@ static void SVFTE_SetupFrames(client_t *client)
|
||||||
client->pendingentities_bits = calloc(client->numpendingentities, sizeof(*client->pendingentities_bits));
|
client->pendingentities_bits = calloc(client->numpendingentities, sizeof(*client->pendingentities_bits));
|
||||||
|
|
||||||
client->pendingentities_bits[0] = UF_REMOVE;
|
client->pendingentities_bits[0] = UF_REMOVE;
|
||||||
|
|
||||||
|
|
||||||
|
client->numpendingcsqcentities = qcvm->num_edicts;
|
||||||
|
client->pendingcsqcentities_bits = calloc(client->numpendingcsqcentities, sizeof(*client->pendingcsqcentities_bits));
|
||||||
}
|
}
|
||||||
static void SVFTE_DroppedFrame(client_t *client, int sequence)
|
static void SVFTE_DroppedFrame(client_t *client, int sequence)
|
||||||
{
|
{
|
||||||
|
@ -873,7 +882,7 @@ static void SVFTE_WriteEntitiesToClient(client_t *client, sizebuf_t *msg, size_t
|
||||||
frame->numents = 0;
|
frame->numents = 0;
|
||||||
if (client->protocol_pext2 & PEXT2_PREDINFO)
|
if (client->protocol_pext2 & PEXT2_PREDINFO)
|
||||||
MSG_WriteShort(msg, (client->lastmovemessage&0xffff));
|
MSG_WriteShort(msg, (client->lastmovemessage&0xffff));
|
||||||
MSG_WriteFloat(msg, qcvm->time); //should be the time the last physics frame was run.
|
MSG_WriteFloat(msg, frame->timestamp); //should be the time the last physics frame was run.
|
||||||
for (entnum = client->snapshotresume; entnum < client->numpendingentities; entnum++)
|
for (entnum = client->snapshotresume; entnum < client->numpendingentities; entnum++)
|
||||||
{
|
{
|
||||||
bits = client->pendingentities_bits[entnum];
|
bits = client->pendingentities_bits[entnum];
|
||||||
|
@ -888,7 +897,7 @@ static void SVFTE_WriteEntitiesToClient(client_t *client, sizebuf_t *msg, size_t
|
||||||
if (entnum > 0x3fff)
|
if (entnum > 0x3fff)
|
||||||
{
|
{
|
||||||
MSG_WriteShort(msg, 0xc000|(entnum&0x3fff));
|
MSG_WriteShort(msg, 0xc000|(entnum&0x3fff));
|
||||||
MSG_WriteShort(msg, entnum>>14);
|
MSG_WriteByte(msg, entnum>>14);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
MSG_WriteShort(msg, 0x8000|entnum);
|
MSG_WriteShort(msg, 0x8000|entnum);
|
||||||
|
@ -921,7 +930,7 @@ static void SVFTE_WriteEntitiesToClient(client_t *client, sizebuf_t *msg, size_t
|
||||||
if (entnum >= 0x4000)
|
if (entnum >= 0x4000)
|
||||||
{
|
{
|
||||||
MSG_WriteShort(msg, 0x4000|(entnum&0x3fff));
|
MSG_WriteShort(msg, 0x4000|(entnum&0x3fff));
|
||||||
MSG_WriteShort(msg, entnum>>14);
|
MSG_WriteByte(msg, entnum>>14);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
MSG_WriteShort(msg, entnum);
|
MSG_WriteShort(msg, entnum);
|
||||||
|
@ -943,13 +952,14 @@ static void SVFTE_WriteEntitiesToClient(client_t *client, sizebuf_t *msg, size_t
|
||||||
}
|
}
|
||||||
frame->ents[frame->numents].num = entnum;
|
frame->ents[frame->numents].num = entnum;
|
||||||
frame->ents[frame->numents].bits = logbits;
|
frame->ents[frame->numents].bits = logbits;
|
||||||
|
frame->ents[frame->numents].csqcbits = 0;
|
||||||
frame->numents++;
|
frame->numents++;
|
||||||
}
|
}
|
||||||
msg->maxsize = origmaxsize;
|
msg->maxsize = origmaxsize;
|
||||||
MSG_WriteShort(msg, 0); //eom
|
MSG_WriteShort(msg, 0); //eom
|
||||||
|
|
||||||
//remember how far we got, so we can keep things flushed, instead of only updating the first N entities.
|
//remember how far we got, so we can keep things flushed, instead of only updating the first N entities.
|
||||||
client->snapshotresume = (entnum<client->numpendingentities?entnum:0);
|
client->snapshotresume = entnum;
|
||||||
|
|
||||||
|
|
||||||
if (msg->cursize > 1024 && dev_peakstats.packetsize <= 1024)
|
if (msg->cursize > 1024 && dev_peakstats.packetsize <= 1024)
|
||||||
|
@ -958,6 +968,127 @@ static void SVFTE_WriteEntitiesToClient(client_t *client, sizebuf_t *msg, size_t
|
||||||
dev_peakstats.packetsize = q_max(msg->cursize, dev_peakstats.packetsize);
|
dev_peakstats.packetsize = q_max(msg->cursize, dev_peakstats.packetsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void SVFTE_WriteCSQCEntitiesToClient(client_t *client, sizebuf_t *msg, size_t overflowsize)
|
||||||
|
{
|
||||||
|
edict_t *ed;
|
||||||
|
unsigned int bits, logbits;
|
||||||
|
size_t entnum;
|
||||||
|
int sequence = NET_QSocketGetSequenceOut(client->netconnection);
|
||||||
|
size_t origmaxsize = msg->maxsize;
|
||||||
|
size_t rollbacksize; //I'm too lazy to figure out sizes (especially if someone updates this for bone states or whatever)
|
||||||
|
struct deltaframe_s *frame = &client->frames[sequence&(client->numframes-1)];
|
||||||
|
|
||||||
|
qboolean wroteheader = false;
|
||||||
|
|
||||||
|
if (!GetEdictFieldValid(SendEntity) || !GetEdictFieldValid(SendFlags))
|
||||||
|
return; //mod does not participate in csqc entity networking.
|
||||||
|
|
||||||
|
msg->maxsize = overflowsize;
|
||||||
|
|
||||||
|
for (entnum = 1; entnum < client->numpendingcsqcentities; entnum++)
|
||||||
|
{
|
||||||
|
bits = client->pendingcsqcentities_bits[entnum];
|
||||||
|
if (!(bits & ~SENDFLAG_PRESENT))
|
||||||
|
continue; //nothing to send (if reset2 is still set, then leave it pending until there's more data
|
||||||
|
|
||||||
|
rollbacksize = msg->cursize;
|
||||||
|
logbits = 0;
|
||||||
|
if (bits & SENDFLAG_REMOVE)
|
||||||
|
{
|
||||||
|
sendremove:
|
||||||
|
// if (GetEdictFieldValid(pvsflags) && (int)GetEdictFieldEval(EDICT_NUM(entnum), pvsflags)->_float & 0x80)
|
||||||
|
// continue;
|
||||||
|
if (!wroteheader)
|
||||||
|
{
|
||||||
|
MSG_WriteByte(msg, svcdp_csqcentities);
|
||||||
|
wroteheader = true;
|
||||||
|
}
|
||||||
|
if (entnum > 0x3fff)
|
||||||
|
{
|
||||||
|
MSG_WriteShort(msg, 0xc000|(entnum&0x3fff));
|
||||||
|
MSG_WriteByte(msg, entnum>>14);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MSG_WriteShort(msg, 0x8000|entnum);
|
||||||
|
logbits = SENDFLAG_REMOVE;
|
||||||
|
bits = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ed = EDICT_NUM(entnum);
|
||||||
|
if (ed->free)
|
||||||
|
{
|
||||||
|
if (bits & SENDFLAG_PRESENT)
|
||||||
|
goto sendremove;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sv.multicast.cursize = 0; //just in case
|
||||||
|
|
||||||
|
pr_global_struct->self = EDICT_TO_PROG(ed);
|
||||||
|
G_INT(OFS_PARM0) = EDICT_TO_PROG(client->edict);
|
||||||
|
G_FLOAT(OFS_PARM1) = bits & SENDFLAG_USABLE;
|
||||||
|
PR_ExecuteProgram(GetEdictFieldEval(ed, SendEntity)->function);
|
||||||
|
if (G_FLOAT(OFS_RETURN))
|
||||||
|
{
|
||||||
|
if (!wroteheader)
|
||||||
|
{
|
||||||
|
MSG_WriteByte(msg, svcdp_csqcentities);
|
||||||
|
wroteheader = true;
|
||||||
|
}
|
||||||
|
if (entnum >= 0x4000)
|
||||||
|
{
|
||||||
|
MSG_WriteShort(msg, 0x4000|(entnum&0x3fff));
|
||||||
|
MSG_WriteByte(msg, entnum>>14);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
MSG_WriteShort(msg, entnum);
|
||||||
|
|
||||||
|
//we're just borrowing the multicast buffer...
|
||||||
|
SZ_Write(msg, sv.multicast.data, sv.multicast.cursize);
|
||||||
|
|
||||||
|
logbits = bits;
|
||||||
|
bits = SENDFLAG_PRESENT;
|
||||||
|
}
|
||||||
|
else if (bits & SENDFLAG_PRESENT)
|
||||||
|
goto sendremove;
|
||||||
|
else
|
||||||
|
logbits = bits = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
client->pendingcsqcentities_bits[entnum] = bits;
|
||||||
|
|
||||||
|
if ((size_t)msg->cursize + 2 > origmaxsize)
|
||||||
|
{
|
||||||
|
msg->cursize = rollbacksize; //roll back
|
||||||
|
client->pendingentities_bits[entnum] |= logbits; //make sure those bits get re-applied later.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (logbits)
|
||||||
|
{
|
||||||
|
if (frame->numents == frame->maxents)
|
||||||
|
{
|
||||||
|
frame->maxents += 64;
|
||||||
|
frame->ents = realloc(frame->ents, sizeof(*frame->ents)*frame->maxents);
|
||||||
|
}
|
||||||
|
frame->ents[frame->numents].num = entnum;
|
||||||
|
frame->ents[frame->numents].bits = 0;
|
||||||
|
frame->ents[frame->numents].csqcbits = logbits;
|
||||||
|
frame->numents++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
msg->maxsize = origmaxsize;
|
||||||
|
if (wroteheader)
|
||||||
|
MSG_WriteShort(msg, 0); //eom
|
||||||
|
sv.multicast.cursize = 0;
|
||||||
|
|
||||||
|
if (msg->cursize > 1024 && dev_peakstats.packetsize <= 1024)
|
||||||
|
Con_DWarning ("%i byte packet exceeds standard limit of 1024.\n", msg->cursize);
|
||||||
|
dev_stats.packetsize = msg->cursize;
|
||||||
|
dev_peakstats.packetsize = q_max(msg->cursize, dev_peakstats.packetsize);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
SV_BuildEntityState
|
SV_BuildEntityState
|
||||||
copies edict state into a more compact entity_state_t with all the extension fields etc sorted out and neatened up for network precision.
|
copies edict state into a more compact entity_state_t with all the extension fields etc sorted out and neatened up for network precision.
|
||||||
|
@ -1026,6 +1157,8 @@ static void SVFTE_BuildSnapshotForClient (client_t *client)
|
||||||
size_t numents = 0;
|
size_t numents = 0;
|
||||||
size_t maxents = snapshot_maxents;
|
size_t maxents = snapshot_maxents;
|
||||||
int emiteffect;
|
int emiteffect;
|
||||||
|
int iscsqc;
|
||||||
|
qboolean cancsqc = GetEdictFieldValid(SendEntity) && GetEdictFieldValid(SendFlags) && client->csqcactive;
|
||||||
|
|
||||||
// find the client's PVS
|
// find the client's PVS
|
||||||
VectorAdd (clent->v.origin, clent->v.view_ofs, org);
|
VectorAdd (clent->v.origin, clent->v.view_ofs, org);
|
||||||
|
@ -1034,23 +1167,37 @@ static void SVFTE_BuildSnapshotForClient (client_t *client)
|
||||||
if (maxentities > (unsigned int)qcvm->num_edicts)
|
if (maxentities > (unsigned int)qcvm->num_edicts)
|
||||||
maxentities = (unsigned int)qcvm->num_edicts;
|
maxentities = (unsigned int)qcvm->num_edicts;
|
||||||
|
|
||||||
|
if ((int)client->numpendingcsqcentities < maxentities)
|
||||||
|
{ //this is the problem with dynamic memory allocations.
|
||||||
|
int newmax = maxentities+64;
|
||||||
|
client->pendingcsqcentities_bits = realloc(client->pendingcsqcentities_bits, sizeof(*client->pendingcsqcentities_bits) * newmax);
|
||||||
|
memset(client->pendingcsqcentities_bits+client->numpendingcsqcentities, 0, sizeof(*client->pendingcsqcentities_bits)*(newmax-client->numpendingcsqcentities));
|
||||||
|
client->numpendingcsqcentities = newmax;
|
||||||
|
}
|
||||||
|
|
||||||
// send over all entities (excpet the client) that touch the pvs
|
// send over all entities (excpet the client) that touch the pvs
|
||||||
ent = NEXT_EDICT(qcvm->edicts);
|
ent = NEXT_EDICT(qcvm->edicts);
|
||||||
for (e=1 ; e<maxentities ; e++, ent = NEXT_EDICT(ent))
|
for (e=1 ; e<maxentities ; e++, ent = NEXT_EDICT(ent))
|
||||||
{
|
{
|
||||||
eflags = 0;
|
eflags = 0;
|
||||||
emiteffect = GetEdictFieldValue(ent, qcvm->extfields.emiteffectnum)->_float;
|
emiteffect = GetEdictFieldValue(ent, qcvm->extfields.emiteffectnum)->_float;
|
||||||
|
iscsqc = cancsqc && GetEdictFieldEval(ent, SendEntity)->function;
|
||||||
if (ent != clent) // clent is ALLWAYS sent
|
if (ent != clent) // clent is ALLWAYS sent
|
||||||
{
|
{
|
||||||
// ignore ents without visible models
|
// ignore ents without visible models
|
||||||
if ((!ent->v.modelindex || !PR_GetString(ent->v.model)[0]) && !emiteffect)
|
if ((!ent->v.modelindex || !PR_GetString(ent->v.model)[0]) && !emiteffect && !iscsqc)
|
||||||
|
{
|
||||||
|
invisible:
|
||||||
|
if (client->pendingcsqcentities_bits[e] /*&& !((int)GetEdictFieldEval(ent, pvsflags)->_float & PVFS_NOREMOVE)*/)
|
||||||
|
client->pendingcsqcentities_bits[e] |= SENDFLAG_REMOVE;
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
val = GetEdictFieldValue(ent, qcvm->extfields.viewmodelforclient);
|
val = GetEdictFieldValue(ent, qcvm->extfields.viewmodelforclient);
|
||||||
if (val && val->edict == proged)
|
if (val && val->edict == proged)
|
||||||
eflags |= EFLAGS_VIEWMODEL;
|
eflags |= EFLAGS_VIEWMODEL;
|
||||||
else if (val && val->edict)
|
else if (val && val->edict)
|
||||||
continue;
|
goto invisible;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//attached entities should use the pvs of the parent rather than the child (because the child will typically be bugging out around '0 0 0', so won't be useful)
|
//attached entities should use the pvs of the parent rather than the child (because the child will typically be bugging out around '0 0 0', so won't be useful)
|
||||||
|
@ -1071,13 +1218,24 @@ static void SVFTE_BuildSnapshotForClient (client_t *client)
|
||||||
// this commonly happens with rotators, because they often have huge bboxes
|
// this commonly happens with rotators, because they often have huge bboxes
|
||||||
// spanning the entire map, or really tall lifts, etc.
|
// spanning the entire map, or really tall lifts, etc.
|
||||||
if (i == parent->num_leafs && parent->num_leafs < MAX_ENT_LEAFS)
|
if (i == parent->num_leafs && parent->num_leafs < MAX_ENT_LEAFS)
|
||||||
continue; // not visible
|
goto invisible; // not visible
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//okay, we care about this entity.
|
//okay, we care about this entity.
|
||||||
|
|
||||||
|
if (iscsqc)
|
||||||
|
{
|
||||||
|
if (!(client->pendingcsqcentities_bits[e] & SENDFLAG_PRESENT))
|
||||||
|
client->pendingcsqcentities_bits[e] |= SENDFLAG_USABLE; //this ent is new. be sure to flag ALL bits.
|
||||||
|
else
|
||||||
|
client->pendingcsqcentities_bits[e] |= (int)GetEdictFieldEval(ent, SendFlags)->_float & SENDFLAG_USABLE; //let the SendEntity function know which fields need to be updated.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (client->pendingcsqcentities_bits[e])
|
||||||
|
client->pendingcsqcentities_bits[e] |= SENDFLAG_REMOVE;
|
||||||
|
|
||||||
if (numents == maxents)
|
if (numents == maxents)
|
||||||
{
|
{
|
||||||
maxents += 64;
|
maxents += 64;
|
||||||
|
@ -2296,9 +2454,21 @@ void SV_CleanupEnts (void)
|
||||||
edict_t *ent;
|
edict_t *ent;
|
||||||
|
|
||||||
ent = NEXT_EDICT(qcvm->edicts);
|
ent = NEXT_EDICT(qcvm->edicts);
|
||||||
|
|
||||||
|
if (GetEdictFieldValid(SendFlags))
|
||||||
|
{
|
||||||
for (e=1 ; e<qcvm->num_edicts ; e++, ent = NEXT_EDICT(ent))
|
for (e=1 ; e<qcvm->num_edicts ; e++, ent = NEXT_EDICT(ent))
|
||||||
{
|
{
|
||||||
ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH;
|
ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH;
|
||||||
|
GetEdictFieldEval(ent, SendFlags)->_float = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (e=1 ; e<qcvm->num_edicts ; e++, ent = NEXT_EDICT(ent))
|
||||||
|
{
|
||||||
|
ent->v.effects = (int)ent->v.effects & ~EF_MUZZLEFLASH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2505,6 +2675,20 @@ void SV_WriteClientdataToMessage (client_t *client, sizebuf_t *msg)
|
||||||
//johnfitz
|
//johnfitz
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SV_PresendClientDatagram (client_t *client)
|
||||||
|
{
|
||||||
|
if (!client->netconnection)
|
||||||
|
return; //botclient
|
||||||
|
if (!client->spawned)
|
||||||
|
return; //not ready yet.
|
||||||
|
if (!(client->protocol_pext2 & PEXT2_REPLACEMENTDELTAS))
|
||||||
|
return; //brute force networking.
|
||||||
|
SVFTE_BuildSnapshotForClient(client);
|
||||||
|
SVFTE_CalcEntityDeltas(client);
|
||||||
|
client->snapshotresume = 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
=======================
|
=======================
|
||||||
SV_SendClientDatagram
|
SV_SendClientDatagram
|
||||||
|
@ -2541,21 +2725,18 @@ qboolean SV_SendClientDatagram (client_t *client)
|
||||||
SV_WriteClientdataToMessage (client, &msg);
|
SV_WriteClientdataToMessage (client, &msg);
|
||||||
else
|
else
|
||||||
SVFTE_WriteStats(client, &msg);
|
SVFTE_WriteStats(client, &msg);
|
||||||
if (!client->snapshotresume)
|
|
||||||
{
|
|
||||||
SVFTE_BuildSnapshotForClient(client);
|
|
||||||
SVFTE_CalcEntityDeltas(client);
|
|
||||||
}
|
|
||||||
SVFTE_WriteEntitiesToClient(client, &msg, sizeof(buf)); //must always write some data, or the stats will break
|
SVFTE_WriteEntitiesToClient(client, &msg, sizeof(buf)); //must always write some data, or the stats will break
|
||||||
|
SVFTE_WriteCSQCEntitiesToClient(client, &msg, sizeof(buf));
|
||||||
|
|
||||||
//this delta protocol doesn't wipe old state just because there's a new packet.
|
//this delta protocol doesn't wipe old state just because there's a new packet.
|
||||||
//the server isn't required to sync with the client frames either
|
//the server isn't required to sync with the client frames either
|
||||||
//so we can just spam multiple packets to keep our udp data under the MTU
|
//so we can just spam multiple packets to keep our udp data under the MTU
|
||||||
while (client->snapshotresume)
|
while (client->snapshotresume < client->numpendingentities)
|
||||||
{
|
{
|
||||||
NET_SendUnreliableMessage (client->netconnection, &msg);
|
NET_SendUnreliableMessage (client->netconnection, &msg);
|
||||||
SZ_Clear(&msg);
|
SZ_Clear(&msg);
|
||||||
SVFTE_WriteEntitiesToClient(client, &msg, sizeof(buf));
|
SVFTE_WriteEntitiesToClient(client, &msg, sizeof(buf));
|
||||||
|
SVFTE_WriteCSQCEntitiesToClient(client, &msg, sizeof(buf));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -2772,6 +2953,14 @@ void SV_SendClientMessages (void)
|
||||||
// update frags, names, etc
|
// update frags, names, etc
|
||||||
SV_UpdateToReliableMessages ();
|
SV_UpdateToReliableMessages ();
|
||||||
|
|
||||||
|
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
||||||
|
{
|
||||||
|
if (!host_client->active)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SV_PresendClientDatagram (host_client); //generates client snapshots (and updates csqc pending flags)
|
||||||
|
}
|
||||||
|
|
||||||
// build individual updates
|
// build individual updates
|
||||||
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
for (i=0, host_client = svs.clients ; i<svs.maxclients ; i++, host_client++)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue