From 0f8d07b8e8a0a9c12b005cfae2f5b5a940e5dfb8 Mon Sep 17 00:00:00 2001 From: Spoike Date: Sat, 28 May 2022 17:59:26 +0000 Subject: [PATCH] Add lerptime protocol extension. Still WIP, don't record to demos. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@6247 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/client/cl_ents.c | 141 +++++++++++++++++------- engine/common/net_chan.c | 7 +- engine/common/protocol.h | 19 ++-- engine/server/pr_cmds.c | 4 +- engine/server/sv_ccmds.c | 12 ++- engine/server/sv_ents.c | 227 ++++++++++++++++++++++++++------------- engine/server/sv_main.c | 10 +- engine/server/sv_user.c | 10 +- 8 files changed, 293 insertions(+), 137 deletions(-) diff --git a/engine/client/cl_ents.c b/engine/client/cl_ents.c index a1875ef69..d535ef206 100644 --- a/engine/client/cl_ents.c +++ b/engine/client/cl_ents.c @@ -630,6 +630,8 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * bits |= MSG_ReadByte()<<16; if (bits & UF_EXTEND3) bits |= MSG_ReadByte()<<24; + if (bits & UF_EXTEND4) + Host_EndGame("ent update bit %#x\n", UF_EXTEND4); if (cl_shownet.ival >= 3) Con_Printf("%3i: Update %4i 0x%x\n", MSG_GetReadCount(), entnum, bits); @@ -653,10 +655,15 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * if (bits & UF_FRAME) { - if (bits & UF_16BIT) - news->frame = MSG_ReadShort(); + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + news->frame = MSG_ReadULEB128(); else - news->frame = MSG_ReadByte(); + { + if (bits & UF_16BIT_LERPTIME) + news->frame = MSG_ReadShort(); + else + news->frame = MSG_ReadByte(); + } } if (cls.ezprotocolextensions1 & EZPEXT1_FLOATENTCOORDS) @@ -702,12 +709,24 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * news->angles[1] = MSG_ReadAngle(); } - if ((bits & (UF_EFFECTS | UF_EFFECTS2)) == (UF_EFFECTS | UF_EFFECTS2)) - news->effects = MSG_ReadLong(); - else if (bits & UF_EFFECTS2) - news->effects = (unsigned short)MSG_ReadShort(); - else if (bits & UF_EFFECTS) - news->effects = MSG_ReadByte(); + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + { + if (bits & UF_16BIT_LERPTIME) + news->lerpend = cl.gametime + MSG_ReadULEB128()*(1/1000.0); //most things will animate at 100ms, so this will usually fit a single byte, without capping out. + if (bits & UF_EFFECTS) + news->effects = MSG_ReadULEB128(); + if (bits & UF_EFFECTS2_OLD) + Host_EndGame("Received unexpected (redefined) bit %#x\n", UF_EFFECTS2_OLD); + } + else + { + if ((bits & (UF_EFFECTS | UF_EFFECTS2_OLD)) == (UF_EFFECTS | UF_EFFECTS2_OLD)) + news->effects = MSG_ReadLong(); + else if (bits & UF_EFFECTS2_OLD) + news->effects = (unsigned short)MSG_ReadShort(); + else if (bits & UF_EFFECTS) + news->effects = MSG_ReadByte(); + } news->u.q1.movement[0] = 0; news->u.q1.movement[1] = 0; @@ -792,20 +811,35 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * if (bits & UF_MODEL) { - if (bits & UF_16BIT) - news->modelindex = MSG_ReadShort(); + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + news->modelindex = MSG_ReadULEB128(); else - news->modelindex = MSG_ReadByte(); + { + if (bits & UF_16BIT_LERPTIME) + news->modelindex = MSG_ReadShort(); + else + news->modelindex = MSG_ReadByte(); + } } if (bits & UF_SKIN) { - if (bits & UF_16BIT) - news->skinnum = MSG_ReadShort(); + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + news->skinnum = MSG_ReadULEB128()-64; //biased for content overrides else - news->skinnum = MSG_ReadByte(); + { + if (bits & UF_16BIT_LERPTIME) + news->skinnum = MSG_ReadShort(); + else + news->skinnum = MSG_ReadByte(); + } } if (bits & UF_COLORMAP) - news->colormap = MSG_ReadByte(); + { + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + news->colormap = MSG_ReadULEB128(); + else + news->colormap = MSG_ReadByte(); + } if (bits & UF_SOLID) { @@ -832,7 +866,12 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * } if (bits & UF_FLAGS) - news->dpflags = MSG_ReadByte(); + { + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + news->dpflags = MSG_ReadULEB128(); + else + news->dpflags = MSG_ReadByte(); + } if (bits & UF_ALPHA) news->trans = MSG_ReadByte(); @@ -855,8 +894,16 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * news->bonecount = 0; //oo, it went away. if (fl & 0x40) { - news->basebone = MSG_ReadByte(); - news->baseframe = MSG_ReadShort(); + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + { + news->basebone = MSG_ReadULEB128(); + news->baseframe = MSG_ReadULEB128(); + } + else + { + news->basebone = MSG_ReadByte(); + news->baseframe = MSG_ReadShort(); + } } else { @@ -885,9 +932,14 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * if (bits & UF_TAGINFO) { news->tagentity = MSGCL_ReadEntity(); - news->tagindex = MSG_ReadByte(); - if (news->tagindex == 0xff) - news->tagindex = ~0; + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + news->tagindex = MSG_ReadULEB128()-1; //biased for q3-like portals. + else + { + news->tagindex = MSG_ReadByte(); + if (news->tagindex == 0xff) + news->tagindex = ~0; + } } if (bits & UF_LIGHT) { @@ -895,20 +947,32 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * news->light[1] = MSG_ReadShort(); news->light[2] = MSG_ReadShort(); news->light[3] = MSG_ReadShort(); - news->lightstyle = MSG_ReadByte(); + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + news->lightstyle = MSG_ReadULEB128(); + else + news->lightstyle = MSG_ReadByte(); news->lightpflags = MSG_ReadByte(); } if (bits & UF_TRAILEFFECT) { - unsigned short s; - s = MSG_ReadShort(); - news->u.q1.traileffectnum = s & 0x3fff; + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + { + news->u.q1.traileffectnum = MSG_ReadULEB128(); + news->u.q1.emiteffectnum = MSG_ReadULEB128(); + } + else + { + unsigned short s; + s = MSG_ReadShort(); + news->u.q1.traileffectnum = s & 0x3fff; + if (s & 0x8000) + news->u.q1.emiteffectnum = MSG_ReadShort() & 0x3fff; + else + news->u.q1.emiteffectnum = 0; + } + if (news->u.q1.traileffectnum >= countof(cl.particle_ssprecache)) news->u.q1.traileffectnum = 0; - if (s & 0x8000) - news->u.q1.emiteffectnum = MSG_ReadShort() & 0x3fff; - else - news->u.q1.emiteffectnum = 0; if (news->u.q1.emiteffectnum >= countof(cl.particle_ssprecache)) news->u.q1.emiteffectnum = 0; } @@ -931,23 +995,24 @@ void CLFTE_ReadDelta(unsigned int entnum, entity_state_t *news, entity_state_t * news->fatness = MSG_ReadByte(); if (bits & UF_MODELINDEX2) { - if (bits & UF_16BIT) - news->modelindex2 = MSG_ReadShort(); + if (cls.fteprotocolextensions2 & PEXT2_LERPTIME) + news->modelindex2 = MSG_ReadULEB128(); else - news->modelindex2 = MSG_ReadByte(); + { + if (bits & UF_16BIT_LERPTIME) + news->modelindex2 = MSG_ReadShort(); + else + news->modelindex2 = MSG_ReadByte(); + } } if (bits & UF_GRAVITYDIR) { news->u.q1.gravitydir[0] = MSG_ReadByte(); news->u.q1.gravitydir[1] = MSG_ReadByte(); } - if (bits & UF_UNUSED2) - { - Host_EndGame("UF_UNUSED2 bit\n"); - } if (bits & UF_UNUSED1) { - Host_EndGame("UF_UNUSED1 bit\n"); + Host_EndGame("ent update bit %#x\n", UF_UNUSED1); } } diff --git a/engine/common/net_chan.c b/engine/common/net_chan.c index e37cac15a..6d9a6911c 100644 --- a/engine/common/net_chan.c +++ b/engine/common/net_chan.c @@ -90,6 +90,7 @@ cvar_t net_mtu = CVARD("net_mtu", "1440", "Specifies a maximum udp payload size, cvar_t net_compress = CVARD("net_compress", "0", "Enables huffman compression of network packets."); cvar_t pext_vrinputs = CVARD("_pext_vrinputs", "0", "RENAME ME WHEN STABLE. Networks player inputs slightly differently, allowing for greater capabilities, particuarly vr controller info."); +cvar_t pext_lerptime = CVARD("_pext_lerptime", "0", "RENAME ME WHEN STABLE. Sends timing hints for interpolation."); cvar_t pext_infoblobs = CVARD("_pext_infoblobs", "0", "RENAME ME WHEN STABLE. Enables the use of very large infokeys containing potentially invalid chars. Note that the userinfo is still limited by sv_userinfo_bytelimit and sv_userinfo_keylimit."); cvar_t pext_replacementdeltas = CVARD("pext_replacementdeltas", "1", "Enables the use of alternative nack-based entity deltas"); cvar_t pext_predinfo = CVARD("pext_predinfo", "1", "Enables some extra things to support prediction over NQ protocols."); @@ -225,6 +226,9 @@ unsigned int Net_PextMask(unsigned int protover, qboolean fornq) if (pext_vrinputs.ival) mask |= PEXT2_VRINPUTS; + if (pext_lerptime.ival) + mask |= PEXT2_LERPTIME; + if (MAX_CLIENTS != QWMAX_CLIENTS) mask |= PEXT2_MAXPLAYERS; @@ -236,7 +240,7 @@ unsigned int Net_PextMask(unsigned int protover, qboolean fornq) if (fornq) { //only ones that are tested - mask &= PEXT2_PRYDONCURSOR | PEXT2_VOICECHAT | PEXT2_SETANGLEDELTA | PEXT2_REPLACEMENTDELTAS | PEXT2_MAXPLAYERS | PEXT2_PREDINFO | PEXT2_NEWSIZEENCODING | PEXT2_VRINPUTS; + mask &= PEXT2_PRYDONCURSOR | PEXT2_VOICECHAT | PEXT2_SETANGLEDELTA | PEXT2_REPLACEMENTDELTAS | PEXT2_MAXPLAYERS | PEXT2_PREDINFO | PEXT2_NEWSIZEENCODING | PEXT2_VRINPUTS | PEXT2_LERPTIME; } // else // mask &= ~PEXT2_PREDINFO; @@ -281,6 +285,7 @@ void Netchan_Init (void) Cvar_Register (&pext_replacementdeltas, "Protocol Extensions"); Cvar_Register (&pext_infoblobs, "Protocol Extensions"); Cvar_Register (&pext_vrinputs, "Protocol Extensions"); + Cvar_Register (&pext_lerptime, "Protocol Extensions"); Cvar_Register (&showpackets, "Networking"); Cvar_Register (&showdrop, "Networking"); Cvar_Register (&qport, "Networking"); diff --git a/engine/common/protocol.h b/engine/common/protocol.h index 743b4792e..df9ced935 100644 --- a/engine/common/protocol.h +++ b/engine/common/protocol.h @@ -56,6 +56,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PEXT_CHUNKEDDOWNLOADS 0x20000000 //alternate file download method. Hopefully it'll give quadroupled download speed, especially on higher pings. #define PEXT_CSQC 0x40000000 //csqc additions #define PEXT_DPFLAGS 0x80000000 //extra flags for viewmodel/externalmodel and possible other persistant style flags. +#define PEXT_SERVERADVERTISE ~0u #define PEXT_CLIENTSUPPORT (PEXT_SETVIEW|PEXT_SCALE|PEXT_LIGHTSTYLECOL|PEXT_TRANS|PEXT_VIEW2_|PEXT_ACCURATETIMINGS|PEXT_SOUNDDBL|PEXT_FATNESS|PEXT_HLBSP|PEXT_TE_BULLET|PEXT_HULLSIZE|PEXT_MODELDBL|PEXT_ENTITYDBL|PEXT_ENTITYDBL2|PEXT_FLOATCOORDS|PEXT_Q2BSP_|PEXT_Q3BSP_|PEXT_COLOURMOD|PEXT_SPLITSCREEN|PEXT_HEXEN2|PEXT_SPAWNSTATIC2|PEXT_CUSTOMTEMPEFFECTS|PEXT_256PACKETENTITIES|PEXT_SHOWPIC|PEXT_SETATTACHMENT|PEXT_CHUNKEDDOWNLOADS|PEXT_CSQC|PEXT_DPFLAGS) #ifdef CSQC_DAT @@ -84,7 +85,9 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #define PEXT2_INFOBLOBS 0x00000080 //serverinfo+userinfo lengths can be MUCH higher (protocol is unbounded, but expect low sanity limits on userinfo), and contain nulls etc. #define PEXT2_STUNAWARE 0x00000100 //changes the netchan to biased-bigendian (so lead two bits are 1 and not stun's 0, so we don't get confused) #define PEXT2_VRINPUTS 0x00000200 //clc_move changes, more buttons etc. vr stuff! -#define PEXT2_CLIENTSUPPORT (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING|PEXT2_INFOBLOBS|PEXT2_STUNAWARE|PEXT2_VRINPUTS) +#define PEXT2_LERPTIME 0x00000400 //fitz-bloat parity. redefines UF_16BIT as UF_LERPEND in favour of length coding. +#define PEXT2_SERVERADVERTISE ~0u +#define PEXT2_CLIENTSUPPORT (PEXT2_PRYDONCURSOR|PEXT2_VOICECHAT|PEXT2_SETANGLEDELTA|PEXT2_REPLACEMENTDELTAS|PEXT2_MAXPLAYERS|PEXT2_PREDINFO|PEXT2_NEWSIZEENCODING|PEXT2_INFOBLOBS|PEXT2_STUNAWARE|PEXT2_VRINPUTS|PEXT2_LERPTIME) //warn if we see bits not listed here. //EzQuake/Mvdsv extensions. (use ezquake name, to avoid confusion about .mvd format and its protocol differences) #define EZPEXT1_FLOATENTCOORDS 0x00000001 //quirky - doesn't apply to broadcasts, just players+ents. this gives more precision, but will bug out if you try using it to increase map bounds in ways that may not be immediately apparent. iiuc this was added instead of fixing some inconsistent rounding... @@ -714,8 +717,8 @@ enum { #define UF_EXTEND1 (1u<<7) /*stuff which is common on ent spawning*/ -#define UF_RESET (1u<<8) -#define UF_16BIT (1u<<9) /*within this update, frame/skin/model is 16bit, not part of the deltaing itself*/ +#define UF_RESET (1u<<8) /*client will reset entire strict to its baseline*/ +#define UF_16BIT_LERPTIME (1u<<9) /*either included frame/skin/model is 16bit (not part of the deltaing itself), or there's nextthink info*/ #define UF_MODEL (1u<<10) #define UF_SKIN (1u<<11) #define UF_COLORMAP (1u<<12) @@ -738,9 +741,9 @@ enum { #define UF_FATNESS (1u<<26) #define UF_MODELINDEX2 (1u<<27) #define UF_GRAVITYDIR (1u<<28) -#define UF_EFFECTS2 (1u<<29) -#define UF_UNUSED2 (1u<<30) -#define UF_UNUSED1 (1u<<31) +#define UF_EFFECTS2_OLD (1u<<29) /*specified >8bit effects, replaced with variable length*/ +#define UF_UNUSED1 (1u<<30) +#define UF_EXTEND4 (1u<<31) /*these flags are generally not deltaed as they're changing constantly*/ #define UFP_FORWARD (1u<<0) @@ -753,7 +756,7 @@ enum { #define UFP_WEAPONFRAME_OLD (1u<<7) //no longer used. just a stat now that I rewrote stat deltas. #define UFP_VIEWANGLE (1u<<7) -#define UF_REMOVE UF_16BIT /*special flag, slightly more compact (we can reuse the 16bit flag as its not important)*/ +#define UF_SV_REMOVE UF_16BIT_LERPTIME /*special flag - lerptime isn't delta tracked serverside (reset sent as required with other fields)*/ @@ -1220,7 +1223,7 @@ typedef struct entity_state_s qbyte lightstyle; qbyte lightpflags; - unsigned short tagindex; + unsigned short tagindex; //~0 == weird portal thing. unsigned int tagentity; diff --git a/engine/server/pr_cmds.c b/engine/server/pr_cmds.c index 8e3ea6301..60e570990 100644 --- a/engine/server/pr_cmds.c +++ b/engine/server/pr_cmds.c @@ -7290,7 +7290,7 @@ static void QCBUILTIN PF_checkextension (pubprogfuncs_t *prinst, struct globalva } else if (ext->extensioncheck) { - extcheck_t check = {prinst->parms->user, Net_PextMask(PROTOCOL_VERSION_FTE1, false), Net_PextMask(PROTOCOL_VERSION_FTE2, false)}; + extcheck_t check = {prinst->parms->user, Net_PextMask(PROTOCOL_VERSION_FTE1, false)&PEXT_SERVERADVERTISE, Net_PextMask(PROTOCOL_VERSION_FTE2, false)&PEXT2_SERVERADVERTISE}; if (!ext->extensioncheck(&check)) return; //blocked by some setting somewhere, somehow. } @@ -12334,7 +12334,7 @@ void PR_SVExtensionList_f(void) int num; char biissues[8192]; - extcheck_t extcheck = {&sv.world, Net_PextMask(PROTOCOL_VERSION_FTE1, false), Net_PextMask(PROTOCOL_VERSION_FTE2, false)}; + extcheck_t extcheck = {&sv.world, Net_PextMask(PROTOCOL_VERSION_FTE1, false)&PEXT_SERVERADVERTISE, Net_PextMask(PROTOCOL_VERSION_FTE2, false)&PEXT2_SERVERADVERTISE}; #define SHOW_ACTIVEEXT 1 #define SHOW_ACTIVEBI 2 diff --git a/engine/server/sv_ccmds.c b/engine/server/sv_ccmds.c index b4081dc24..bc9dcd242 100644 --- a/engine/server/sv_ccmds.c +++ b/engine/server/sv_ccmds.c @@ -1069,7 +1069,7 @@ void SV_Map_f (void) host_client->sentents.num_entities = 0; host_client->ratetime = 0; if (host_client->pendingdeltabits) - host_client->pendingdeltabits[0] = UF_REMOVE; + host_client->pendingdeltabits[0] = UF_SV_REMOVE; if (flushparms) { @@ -2156,7 +2156,15 @@ static void SV_Status_f (void) if (NET_GetRates(svs.sockets, &pi, &po, &bi, &bo)) Con_TPrintf("packets,bytes/sec: in: %g %g out: %g %g\n", pi, bi, po, bo); //not relevent as a limit. Con_TPrintf("server uptime : %s\n", ShowTime(realtime)); - Con_TPrintf("public : %s\n", sv_public.value?"yes":"no"); + if (sv_public.ival < 0) + s = "hidden"; + else if (sv_public.ival == 2) + s = "hole punching"; + else if (sv_public.ival) + s = "direct"; + else + s = "private"; + Con_TPrintf("public : %s\n", s); switch(svs.gametype) { #ifdef Q3SERVER diff --git a/engine/server/sv_ents.c b/engine/server/sv_ents.c index a0a797da9..b8aa7a0e4 100644 --- a/engine/server/sv_ents.c +++ b/engine/server/sv_ents.c @@ -937,13 +937,14 @@ void SVQW_WriteDelta (entity_state_t *from, entity_state_t *to, sizebuf_t *msg, MSG_WriteByte (msg, (to->effects&0xff00)>>8); } -/*special flags which are slightly more compact. these are 'wasted' as part of the delta itself*/ -#define UF_REMOVE UF_16BIT /*says we removed the entity in this frame*/ -#define UF_MOVETYPE UF_EFFECTS2 /*this flag isn't present in the header itself*/ -#define UF_RESET2 UF_EXTEND1 /*so new ents are reset 3 times to avoid weird baselines*/ -//#define UF_UNUSED UF_EXTEND2 /**/ -#define UF_WEAPONFRAME_OLD UF_EXTEND2 -#define UF_VIEWANGLES UF_EXTEND3 /**/ +/*special flags which are slightly more compact. these are 'wasted' as part of the delta itself. These meanings will not be transmitted.*/ +#define UF_SV_REMOVE UF_16BIT_LERPTIME /*says we removed the entity in this frame*/ +#define UF_SV_RESET2 UF_EXTEND1 /*so new ents are reset 3 times to avoid weird baselines*/ +/*we need some extra bits for the predinfo section too...*/ +#define UF_SV_WEAPONFRAME_OLD UF_EXTEND2 +#define UF_SV_VIEWANGLES UF_EXTEND3 /**/ +#define UF_SV_MOVETYPE UF_EXTEND4 /*this flag isn't present in the header itself*/ +#define UF_SV_ALLFLAGS (UF_SV_REMOVE|UF_SV_RESET2|UF_SV_WEAPONFRAME_OLD|UF_SV_VIEWANGLES|UF_SV_MOVETYPE) static unsigned int SVFTE_DeltaPredCalcBits(entity_state_t *from, entity_state_t *to) { @@ -969,14 +970,14 @@ static unsigned int SVFTE_DeltaPredCalcBits(entity_state_t *from, entity_state_t return bits; } -static unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, qbyte *frombonedata, entity_state_t *to, qbyte *tobonedata) +static unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, qbyte *frombonedata, entity_state_t *to, qbyte *tobonedata, unsigned int pext2) { unsigned int bits = 0; if (from->u.q1.pmovetype != to->u.q1.pmovetype) - bits |= UF_PREDINFO|UF_MOVETYPE; - if (from->u.q1.weaponframe != to->u.q1.weaponframe) - bits |= UF_PREDINFO|UF_WEAPONFRAME_OLD; + bits |= UF_PREDINFO|UF_SV_MOVETYPE; + if (from->u.q1.weaponframe != to->u.q1.weaponframe && !(pext2 & PEXT2_PREDINFO)) + bits |= UF_PREDINFO|UF_SV_WEAPONFRAME_OLD; if (to->u.q1.pmovetype) { if (SVFTE_DeltaPredCalcBits(from, to)) @@ -1033,10 +1034,13 @@ static unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, qbyte *frombonedat if (to->hexen2flags != from->hexen2flags || to->abslight != from->abslight) bits |= UF_DRAWFLAGS; - if (to->bonecount != from->bonecount || (to->bonecount && memcmp(frombonedata+from->boneoffset, tobonedata+to->boneoffset, to->bonecount*sizeof(short)*7))) - bits |= UF_BONEDATA; - if (!to->bonecount && (to->basebone != from->basebone || to->baseframe != from->baseframe)) - bits |= UF_BONEDATA; + if (pext2 & PEXT2_NEWSIZEENCODING) + { //don't flag it unless its actually present. + if (to->bonecount != from->bonecount || (to->bonecount && memcmp(frombonedata+from->boneoffset, tobonedata+to->boneoffset, to->bonecount*sizeof(short)*7))) + bits |= UF_BONEDATA; + if (!to->bonecount && (to->basebone != from->basebone || to->baseframe != from->baseframe)) + bits |= UF_BONEDATA; + } if (to->colormod[0]!=from->colormod[0]||to->colormod[1]!=from->colormod[1]||to->colormod[2]!=from->colormod[2]) bits |= UF_COLORMOD; @@ -1065,30 +1069,30 @@ static unsigned int SVFTE_DeltaCalcBits(entity_state_t *from, qbyte *frombonedat static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_t *msg, unsigned int pext2, unsigned int ezext1, qbyte *boneptr) { unsigned int predbits = 0; - if (bits & UF_MOVETYPE) + if (bits & UF_SV_MOVETYPE) { - bits &= ~UF_MOVETYPE; + bits &= ~UF_SV_MOVETYPE; predbits |= UFP_MOVETYPE; } if (pext2 & PEXT2_PREDINFO) { - if (bits & UF_VIEWANGLES) + if (bits & UF_SV_VIEWANGLES) { - bits &= ~UF_VIEWANGLES; + bits &= ~UF_SV_VIEWANGLES; bits |= UF_PREDINFO; predbits |= UFP_VIEWANGLE; } } else { - if (bits & UF_VIEWANGLES) + if (bits & UF_SV_VIEWANGLES) { - bits &= ~UF_VIEWANGLES; + bits &= ~UF_SV_VIEWANGLES; bits |= UF_PREDINFO; } - if (bits & UF_WEAPONFRAME_OLD) + if (bits & UF_SV_WEAPONFRAME_OLD) { - bits &= ~UF_WEAPONFRAME_OLD; + bits &= ~UF_SV_WEAPONFRAME_OLD; predbits |= UFP_WEAPONFRAME_OLD; } } @@ -1096,21 +1100,37 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ if (!(pext2 & PEXT2_NEWSIZEENCODING)) //was added at the same time bits &= ~UF_BONEDATA; - /*check if we need more precision*/ - if ((bits & UF_MODEL) && state->modelindex > 255) - bits |= UF_16BIT; - if ((bits & UF_SKIN) && state->skinnum > 255) - bits |= UF_16BIT; - if ((bits & UF_FRAME) && state->frame > 255) - bits |= UF_16BIT; +#ifdef _DEBUG + if (bits & UF_SV_ALLFLAGS) //if any of these are set here then we're bloating where we shouldn't be. + Host_EndGame("Server-Only bits shouldn't be set - %#x\n", bits & UF_SV_ALLFLAGS); +#endif - /*convert effects bits to higher lengths if needed*/ - if (bits & UF_EFFECTS) + if (pext2 & PEXT2_LERPTIME) { - if (state->effects & 0xffff0000) /*both*/ - bits |= UF_EFFECTS | UF_EFFECTS2; - else if (state->effects & 0x0000ff00) /*2 only*/ - bits = (bits & ~UF_EFFECTS) | UF_EFFECTS2; + if (bits & (UF_FRAME|UF_MODEL|UF_RESET|UF_ANGLESXZ|UF_ANGLESY)) + if (state->lerpend > sv.world.physicstime) + bits |= UF_16BIT_LERPTIME; + } + else + { + /*check if we need more precision*/ + if ((bits & UF_MODEL) && state->modelindex > 255) + bits |= UF_16BIT_LERPTIME; + if ((bits & UF_MODELINDEX2) && state->modelindex2 > 255) + bits |= UF_16BIT_LERPTIME; + if ((bits & UF_SKIN) && state->skinnum > 255) + bits |= UF_16BIT_LERPTIME; + if ((bits & UF_FRAME) && state->frame > 255) + bits |= UF_16BIT_LERPTIME; + + /*convert effects bits to higher lengths if needed*/ + if (bits & UF_EFFECTS) + { + if (state->effects & 0xffff0000) /*both*/ + bits |= UF_EFFECTS | UF_EFFECTS2_OLD; + else if (state->effects & 0x0000ff00) /*2 only*/ + bits = (bits & ~UF_EFFECTS) | UF_EFFECTS2_OLD; + } } if (bits & 0xff000000) bits |= UF_EXTEND3; @@ -1129,7 +1149,9 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ if (bits & UF_FRAME) { - if (bits & UF_16BIT) + if (pext2 & PEXT2_LERPTIME) + MSG_WriteULEB128(msg, state->frame); + else if (bits & UF_16BIT_LERPTIME) MSG_WriteShort(msg, state->frame); else MSG_WriteByte(msg, state->frame); @@ -1177,12 +1199,27 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ MSG_WriteAngle(msg, state->angles[1]); } - if ((bits & (UF_EFFECTS|UF_EFFECTS2)) == (UF_EFFECTS|UF_EFFECTS2)) - MSG_WriteLong(msg, state->effects); - else if (bits & UF_EFFECTS2) - MSG_WriteShort(msg, state->effects); - else if (bits & UF_EFFECTS) - MSG_WriteByte(msg, state->effects); + if (pext2 & PEXT2_LERPTIME) + { + if (bits & UF_16BIT_LERPTIME) + { + int t = (state->lerpend - sv.world.physicstime)*1000; + MSG_WriteULEB128(msg, max(0, t)); + } + if (bits & UF_EFFECTS) + MSG_WriteULEB128(msg, state->effects); +// if (bits & UF_EFFECTS2) +// MSG_WriteSomething(msg, state->something); + } + else + { + if ((bits & (UF_EFFECTS|UF_EFFECTS2_OLD)) == (UF_EFFECTS|UF_EFFECTS2_OLD)) + MSG_WriteLong(msg, state->effects); + else if (bits & UF_EFFECTS2_OLD) + MSG_WriteShort(msg, state->effects); + else if (bits & UF_EFFECTS) + MSG_WriteByte(msg, state->effects); + } if (bits & UF_PREDINFO) { @@ -1237,20 +1274,29 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ if (bits & UF_MODEL) { - if (bits & UF_16BIT) + if (pext2 & PEXT2_LERPTIME) + MSG_WriteULEB128(msg, state->modelindex); + else if (bits & UF_16BIT_LERPTIME) MSG_WriteShort(msg, state->modelindex); else MSG_WriteByte(msg, state->modelindex); } if (bits & UF_SKIN) { - if (bits & UF_16BIT) + if (pext2 & PEXT2_LERPTIME) + MSG_WriteULEB128(msg, state->skinnum+64); //biased for negative content overrides. + else if (bits & UF_16BIT_LERPTIME) MSG_WriteShort(msg, state->skinnum); else MSG_WriteByte(msg, state->skinnum); } if (bits & UF_COLORMAP) - MSG_WriteByte(msg, state->colormap & 0xff); + { + if (pext2 & PEXT2_LERPTIME) + MSG_WriteULEB128(msg, state->colormap); + else + MSG_WriteByte(msg, state->colormap & 0xff); + } if (bits & UF_SOLID) { @@ -1280,7 +1326,12 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ } if (bits & UF_FLAGS) - MSG_WriteByte(msg, state->dpflags); + { + if (pext2 & PEXT2_LERPTIME) + MSG_WriteULEB128(msg, state->dpflags); + else + MSG_WriteByte(msg, state->dpflags); + } if (bits & UF_ALPHA) MSG_WriteByte(msg, state->trans); @@ -1306,8 +1357,16 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ } if (bfl & 0x40) { - MSG_WriteByte(msg, state->basebone); - MSG_WriteShort(msg, state->baseframe); + if (pext2 & PEXT2_LERPTIME) + { + MSG_WriteULEB128(msg, state->basebone); + MSG_WriteULEB128(msg, state->baseframe); + } + else + { + MSG_WriteByte(msg, state->basebone); + MSG_WriteShort(msg, state->baseframe); + } } } if (bits & UF_DRAWFLAGS) @@ -1319,7 +1378,10 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ if (bits & UF_TAGINFO) { MSG_WriteEntity(msg, state->tagentity); - MSG_WriteByte(msg, state->tagindex&0xff); + if (pext2 & PEXT2_LERPTIME) + MSG_WriteULEB128(msg, state->tagindex+1); //biased when sending to allow for our -1==portal thing + else + MSG_WriteByte(msg, state->tagindex&0xff); } if (bits & UF_LIGHT) { @@ -1327,12 +1389,20 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ MSG_WriteShort (msg, state->light[1]); MSG_WriteShort (msg, state->light[2]); MSG_WriteShort (msg, state->light[3]); - MSG_WriteByte (msg, state->lightstyle); + if (pext2 & PEXT2_LERPTIME) + MSG_WriteULEB128(msg, state->lightstyle); + else + MSG_WriteByte (msg, state->lightstyle); MSG_WriteByte (msg, state->lightpflags); } if (bits & UF_TRAILEFFECT) { - if (state->u.q1.emiteffectnum) + if (pext2 & PEXT2_LERPTIME) + { + MSG_WriteULEB128(msg, state->u.q1.traileffectnum); + MSG_WriteULEB128(msg, state->u.q1.emiteffectnum); + } + else if (state->u.q1.emiteffectnum) { MSG_WriteShort(msg, (state->u.q1.traileffectnum & 0x3fff) | 0x8000); MSG_WriteShort(msg, (state->u.q1.emiteffectnum & 0x3fff)); @@ -1356,10 +1426,12 @@ static void SVFTE_WriteUpdate(unsigned int bits, entity_state_t *state, sizebuf_ MSG_WriteByte(msg, state->glowmod[2]); } if (bits & UF_FATNESS) - MSG_WriteByte(msg, state->fatness); + MSG_WriteChar(msg, state->fatness); if (bits & UF_MODELINDEX2) { - if (bits & UF_16BIT) + if (pext2 & PEXT2_LERPTIME) + MSG_WriteULEB128(msg, state->modelindex2); + else if (bits & UF_16BIT_LERPTIME) MSG_WriteShort(msg, state->modelindex2); else MSG_WriteByte(msg, state->modelindex2); @@ -1378,7 +1450,7 @@ void SVFTE_EmitBaseline(entity_state_t *to, qboolean numberisimportant, sizebuf_ unsigned int bits; if (numberisimportant) MSG_WriteEntity(msg, to->number); - bits = UF_RESET | SVFTE_DeltaCalcBits(&nullentitystate, NULL, to, NULL); + bits = UF_RESET | SVFTE_DeltaCalcBits(&nullentitystate, NULL, to, NULL, pext2); SVFTE_WriteUpdate(bits, to, msg, pext2, ezext, NULL); } @@ -1408,17 +1480,17 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb return false; if (client->delta_sequence < 0) - client->pendingdeltabits[0] = UF_REMOVE; + client->pendingdeltabits[0] = UF_SV_REMOVE; //if we're clearing the list and starting from scratch, just wipe all lingering state - if (client->pendingdeltabits[0] & UF_REMOVE) + if (client->pendingdeltabits[0] & UF_SV_REMOVE) { for (j = 0; j < client->sentents.num_entities; j++) { client->sentents.entities[j].number = 0; client->pendingdeltabits[j] = 0; } - client->pendingdeltabits[0] = UF_REMOVE; + client->pendingdeltabits[0] = UF_SV_REMOVE; } //expand client's entstate list @@ -1467,7 +1539,7 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb e = EDICT_NUM_PB(svprogfuncs, o->number); if (!((int)e->xv->pvsflags & PVSF_NOREMOVE)) { - client->pendingdeltabits[j] = UF_REMOVE; + client->pendingdeltabits[j] = UF_SV_REMOVE; o->number = 0; /*dead*/ o->bonecount = 0; /*don't waste cycles*/ } @@ -1484,14 +1556,14 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb if (!o->number) { /*flag it for reset, we can add the extra bits later once we get around to sending it*/ - client->pendingdeltabits[j] = UF_RESET | UF_RESET2; + client->pendingdeltabits[j] = UF_RESET | UF_SV_RESET2; } else { //its valid, make sure we don't have a stale/resent remove, and do a cheap reset due to uncertainty. - if (client->pendingdeltabits[j] & UF_REMOVE) - client->pendingdeltabits[j] = (client->pendingdeltabits[j] & ~UF_REMOVE) | UF_RESET2; - client->pendingdeltabits[j] |= SVFTE_DeltaCalcBits(o, oldbonedata, n, to->bonedata); + if (client->pendingdeltabits[j] & UF_SV_REMOVE) + client->pendingdeltabits[j] = (client->pendingdeltabits[j] & ~UF_SV_REMOVE) | UF_SV_RESET2; + client->pendingdeltabits[j] |= SVFTE_DeltaCalcBits(o, oldbonedata, n, to->bonedata, client->fteprotocolextensions2); //even if prediction is disabled, we want to force velocity info to be sent for the local player. This is used by view bob and things. if (client->edict && j == client->edict->entnum && (n->u.q1.velocity[0] || n->u.q1.velocity[1] || n->u.q1.velocity[2])) client->pendingdeltabits[j] |= UF_PREDINFO; @@ -1504,7 +1576,7 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb client->pendingdeltabits[j] |= UF_ANGLESXZ; if (o->u.q1.vangle[1] != n->u.q1.vangle[1]) client->pendingdeltabits[j] |= UF_ANGLESY; - client->pendingdeltabits[j] |= UF_VIEWANGLES; + client->pendingdeltabits[j] |= UF_SV_VIEWANGLES; } } *o = *n; @@ -1526,7 +1598,7 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb e = EDICT_NUM_PB(svprogfuncs, o->number); if (!((int)e->xv->pvsflags & PVSF_NOREMOVE)) { - client->pendingdeltabits[j] = UF_REMOVE; + client->pendingdeltabits[j] = UF_SV_REMOVE; o->number = 0; /*dead*/ o->bonecount = 0; /*don't waste cycles*/ } @@ -1602,19 +1674,19 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb // Con_Printf("Gen sequence %i\n", sequence); MSG_WriteFloat(msg, sv.world.physicstime); - if (client->pendingdeltabits[0] & UF_REMOVE) + if (client->pendingdeltabits[0] & UF_SV_REMOVE) { SV_EmitDeltaEntIndex(msg, 0, true, true); - resend[outno].bits = UF_REMOVE; + resend[outno].bits = UF_SV_REMOVE; resend[outno].flags = 0; resend[outno++].entnum = 0; - client->pendingdeltabits[0] &= ~UF_REMOVE; + client->pendingdeltabits[0] &= ~UF_SV_REMOVE; } for(j = 1; j < client->sentents.num_entities; j++) { bits = client->pendingdeltabits[j]; - if (!(bits & ~UF_RESET2)) //skip while there's nothing to send (skip reset2 if there's no other changes, its only to reduce chances of the client getting 'new' entities containing just an origin)*/ + if (!(bits & ~UF_SV_RESET2)) //skip while there's nothing to send (skip reset2 if there's no other changes, its only to reduce chances of the client getting 'new' entities containing just an origin)*/ continue; if (msg->cursize + 52 > msg->maxsize) { @@ -1630,10 +1702,10 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb outmax = frame->maxresend; } - if (bits & UF_REMOVE) + if (bits & UF_SV_REMOVE) { //if reset is set, then reset was set eroneously. SV_EmitDeltaEntIndex(msg, j, true, true); - resend[outno].bits = UF_REMOVE; + resend[outno].bits = UF_SV_REMOVE; // Con_Printf("REMOVE %i @ %i\n", j, sequence); } else if (client->sentents.entities[j].number) /*only send a new copy of the ent if they actually have one already*/ @@ -1643,18 +1715,18 @@ qboolean SVFTE_EmitPacketEntities(client_t *client, packet_entities_t *to, sizeb if (j < client->nextdeltaindex && j > svs.allocated_client_slots) continue; - if (bits & UF_RESET2) + if (bits & UF_SV_RESET2) { /*if reset2, then this is the second packet sent to the client and should have a forced reset (but which isn't tracked)*/ - resend[outno].bits = bits & ~UF_RESET2; - bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata); + resend[outno].bits = bits & ~UF_SV_RESET2; + bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata, client->fteprotocolextensions2); // Con_Printf("RESET2 %i @ %i\n", j, sequence); } else if (bits & UF_RESET) { /*flag the entity for the next packet, so we always get two resets when it appears, to reduce the effects of packetloss on seeing rockets etc*/ - client->pendingdeltabits[j] = UF_RESET2; - bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata); + client->pendingdeltabits[j] = UF_SV_RESET2; + bits = UF_RESET | SVFTE_DeltaCalcBits(&EDICT_NUM_PB(svprogfuncs, j)->baseline, NULL, &client->sentents.entities[j], client->sentents.bonedata, client->fteprotocolextensions2); resend[outno].bits = UF_RESET; // Con_Printf("RESET %i @ %i\n", j, sequence); } @@ -3551,6 +3623,9 @@ void SV_Snapshot_BuildStateQ1(entity_state_t *state, edict_t *ent, client_t *cli if (state->effects & EF_FULLBRIGHT) //wrap the field for fte clients (this is horrible) state->hexen2flags |= MLS_FULLBRIGHT; + if (ent->v->nextthink>sv.world.physicstime) + state->lerpend = ent->v->nextthink; + #ifdef NQPROT if (client && !ISQWCLIENT(client)) { diff --git a/engine/server/sv_main.c b/engine/server/sv_main.c index c7f4ce46d..8ce4ec74d 100644 --- a/engine/server/sv_main.c +++ b/engine/server/sv_main.c @@ -1626,7 +1626,7 @@ qboolean SVC_GetChallenge (qboolean respond_dp) { unsigned int mask; //tell the client what fte extensions we support - mask = Net_PextMask(PROTOCOL_VERSION_FTE1, false); + mask = Net_PextMask(PROTOCOL_VERSION_FTE1, false)&PEXT_SERVERADVERTISE; if (mask) { lng = LittleLong(PROTOCOL_VERSION_FTE1); @@ -1638,7 +1638,7 @@ qboolean SVC_GetChallenge (qboolean respond_dp) over+=sizeof(lng); } //tell the client what fte extensions we support - mask = Net_PextMask(PROTOCOL_VERSION_FTE2, false); + mask = Net_PextMask(PROTOCOL_VERSION_FTE2, false)&PEXT2_SERVERADVERTISE; if (mask) { lng = LittleLong(PROTOCOL_VERSION_FTE2); @@ -2029,8 +2029,8 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) } } - client->fteprotocolextensions &= Net_PextMask(PROTOCOL_VERSION_FTE1, ISNQCLIENT(client)); - client->fteprotocolextensions2 &= Net_PextMask(PROTOCOL_VERSION_FTE2, ISNQCLIENT(client)); + client->fteprotocolextensions &= Net_PextMask(PROTOCOL_VERSION_FTE1, ISNQCLIENT(client)) & PEXT_SERVERADVERTISE; + client->fteprotocolextensions2 &= Net_PextMask(PROTOCOL_VERSION_FTE2, ISNQCLIENT(client)) & PEXT2_SERVERADVERTISE; client->ezprotocolextensions1 &= Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, ISNQCLIENT(client)) & EZPEXT1_SERVERADVERTISE; client->zquake_extensions &= SERVER_SUPPORTED_Z_EXTENSIONS; @@ -2217,7 +2217,7 @@ void SV_ClientProtocolExtensionsChanged(client_t *client) } //make sure the reset is sent. - client->pendingdeltabits[0] = UF_REMOVE; + client->pendingdeltabits[0] = UF_SV_REMOVE; } else if (ISNQCLIENT(client)) { diff --git a/engine/server/sv_user.c b/engine/server/sv_user.c index 1b7b12fa5..7c65d15bd 100644 --- a/engine/server/sv_user.c +++ b/engine/server/sv_user.c @@ -6257,10 +6257,10 @@ void SV_Pext_f(void) switch(strtoul(tag, NULL, 0)) { case PROTOCOL_VERSION_FTE1: - host_client->fteprotocolextensions = strtoul(val, NULL, 0) & Net_PextMask(PROTOCOL_VERSION_FTE1, ISNQCLIENT(host_client)); + host_client->fteprotocolextensions = strtoul(val, NULL, 0) & Net_PextMask(PROTOCOL_VERSION_FTE1, ISNQCLIENT(host_client)) & PEXT_SERVERADVERTISE; break; case PROTOCOL_VERSION_FTE2: - host_client->fteprotocolextensions2 = strtoul(val, NULL, 0) & Net_PextMask(PROTOCOL_VERSION_FTE2, ISNQCLIENT(host_client)); + host_client->fteprotocolextensions2 = strtoul(val, NULL, 0) & Net_PextMask(PROTOCOL_VERSION_FTE2, ISNQCLIENT(host_client)) & PEXT2_SERVERADVERTISE; break; case PROTOCOL_VERSION_EZQUAKE1: host_client->ezprotocolextensions1 = strtoul(val, NULL, 0) & Net_PextMask(PROTOCOL_VERSION_EZQUAKE1, ISNQCLIENT(host_client)) & EZPEXT1_SERVERADVERTISE; @@ -7979,7 +7979,7 @@ static double SVFTE_ExecuteClientMove(client_t *controller) { unsigned int e; if (controller->pendingdeltabits) - controller->pendingdeltabits[0] = UF_REMOVE; + controller->pendingdeltabits[0] = UF_SV_REMOVE; if (host_client->pendingcsqcbits) for (e = 1; e < host_client->max_net_ents; e++) if (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT) @@ -8421,7 +8421,7 @@ void SV_ExecuteClientMessage (client_t *cl) { unsigned int e; if (cl->pendingdeltabits) - cl->pendingdeltabits[0] = UF_REMOVE; + cl->pendingdeltabits[0] = UF_SV_REMOVE; if (host_client->pendingcsqcbits) for (e = 1; e < host_client->max_net_ents; e++) if (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT) @@ -9046,7 +9046,7 @@ void SVNQ_ExecuteClientMessage (client_t *cl) { unsigned int e; if (cl->pendingdeltabits) - cl->pendingdeltabits[0] = UF_REMOVE; + cl->pendingdeltabits[0] = UF_SV_REMOVE; if (host_client->pendingcsqcbits) for (e = 1; e < host_client->max_net_ents; e++) if (host_client->pendingcsqcbits[e] & SENDFLAGS_PRESENT)