mirror of
https://github.com/nzp-team/fteqw.git
synced 2024-12-11 21:11:08 +00:00
b0e12ff2ba
git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@352 fc73d0e0-1445-4013-8a0c-d673dee63da5
2097 lines
44 KiB
C
2097 lines
44 KiB
C
#include "qwsvdef.h"
|
|
|
|
static sizebuf_t *writedest;
|
|
static client_t *cldest;
|
|
static int majortype;
|
|
static int minortype;
|
|
static int protocollen;
|
|
|
|
static qbyte buffer[MAX_QWMSGLEN];
|
|
static int bufferlen;
|
|
static int nullterms;
|
|
|
|
static int multicastpos; //writecoord*3 offset
|
|
static int multicasttype;
|
|
static int requireextension;
|
|
static qboolean ignoreprotocol;
|
|
|
|
#define svc_setname 13 //NQ, not QW
|
|
#define svc_setfrags 14
|
|
#define svc_updatecolors 17
|
|
|
|
#define svc_clearviewflags 41 //hexen2.
|
|
|
|
//these are present in the darkplaces engine.
|
|
//I wanna knick thier mods.
|
|
#define svcdp_skybox 37
|
|
|
|
|
|
#define TE_EXPLOSION3_NEH 16 // [vector] origin [coord] red [coord] green [coord] blue
|
|
#define TE_LIGHTNING4_NEH 17 // [string] model [entity] entity [vector] start [vector] end
|
|
#define TE_EXPLOSIONSMALL2 20 // org.
|
|
|
|
//share with pr_cmds.
|
|
#define MSG_BROADCAST 0 // unreliable to all
|
|
#define MSG_ONE 1 // reliable to one (msg_entity)
|
|
#define MSG_ALL 2 // reliable to all
|
|
#define MSG_INIT 3 // write to the init string
|
|
#define MSG_MULTICAST 4 // for multicast()
|
|
|
|
client_t *Write_GetClient(void);
|
|
sizebuf_t *WriteDest (int dest);
|
|
#ifdef NQPROT
|
|
sizebuf_t *NQWriteDest (int dest);
|
|
#endif
|
|
|
|
void NPP_SetInfo(client_t *cl, char *key, char *value)
|
|
{
|
|
int i;
|
|
Info_SetValueForKey (cl->userinfo, key, value, MAX_INFO_STRING);
|
|
if (!*Info_ValueForKey (cl->userinfo, "name"))
|
|
cl->name[0] = '\0';
|
|
else // process any changed values
|
|
SV_ExtractFromUserinfo (cl);
|
|
|
|
i = cl - svs.clients;
|
|
MSG_WriteByte (&sv.reliable_datagram, svc_setinfo);
|
|
MSG_WriteByte (&sv.reliable_datagram, i);
|
|
MSG_WriteString (&sv.reliable_datagram, key);
|
|
MSG_WriteString (&sv.reliable_datagram, Info_ValueForKey(cl->userinfo, key));
|
|
}
|
|
|
|
void NPP_Flush(void)
|
|
{
|
|
if (!bufferlen)
|
|
return;
|
|
|
|
|
|
switch(majortype)
|
|
{
|
|
case svc_cdtrack:
|
|
if (bufferlen!=protocollen)
|
|
Con_Printf("svc_cdtrack wasn't the right length\n");
|
|
else
|
|
bufferlen-=1;
|
|
break;
|
|
//ignore these.
|
|
case svc_print:
|
|
case svcdp_skybox:
|
|
case svc_setfrags:
|
|
bufferlen = 0;
|
|
break;
|
|
case svc_setname:
|
|
bufferlen = 0;
|
|
NPP_SetInfo(&svs.clients[buffer[1]], "name", buffer+2);
|
|
break;
|
|
case svc_updatecolors:
|
|
bufferlen = 0;
|
|
NPP_SetInfo(&svs.clients[buffer[1]], "bottomcolor", va("%i", buffer[2]&15));
|
|
NPP_SetInfo(&svs.clients[buffer[1]], "topcolor", va("%i", buffer[2]/16));
|
|
break;
|
|
case svc_intermission:
|
|
if (writedest == &sv.reliable_datagram)
|
|
{
|
|
client_t *cl;
|
|
int i;
|
|
for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
|
|
{
|
|
if (cl->state == cs_spawned
|
|
#ifdef NQPROT
|
|
&& !cl->nqprot
|
|
#endif
|
|
)
|
|
{
|
|
if (cl->zquake_extensions & Z_EXT_SERVERTIME)
|
|
{
|
|
ClientReliableCheckBlock(cl, 6);
|
|
ClientReliableWrite_Byte(cl, svc_updatestatlong);
|
|
ClientReliableWrite_Byte(cl, STAT_TIME);
|
|
ClientReliableWrite_Long(cl, (int)(sv.time * 1000));
|
|
cl->nextservertimeupdate = sv.time+10;
|
|
}
|
|
|
|
ClientReliableCheckBlock(cl, 7);
|
|
ClientReliableWrite_Byte(cl, svc_intermission);
|
|
ClientReliableWrite_Coord(cl, cl->edict->v.origin[0]);
|
|
ClientReliableWrite_Coord(cl, cl->edict->v.origin[1]);
|
|
ClientReliableWrite_Coord(cl, cl->edict->v.origin[2]+cl->edict->v.view_ofs[2]);
|
|
ClientReliableWrite_Angle(cl, cl->edict->v.angles[0]);
|
|
ClientReliableWrite_Angle(cl, cl->edict->v.angles[1]);
|
|
ClientReliableWrite_Angle(cl, cl->edict->v.angles[2]);
|
|
|
|
}
|
|
}
|
|
bufferlen = 0;
|
|
protocollen=0;
|
|
writedest = NULL;
|
|
}
|
|
// case svc_finale:
|
|
// bufferlen = 0;
|
|
// break;
|
|
case svc_setview:
|
|
// requireextension = PEXT_SETVIEW;
|
|
|
|
if (cldest) //catch it to work with all clients
|
|
{
|
|
cldest->viewent = *(unsigned short*)&buffer[1];
|
|
// bufferlen = 0;
|
|
if (cldest->viewent == (cldest - svs.clients)+1)
|
|
cldest->viewent = 0; //self is the same as none
|
|
}
|
|
bufferlen = 0;
|
|
break;
|
|
case svc_temp_entity:
|
|
switch (buffer[1])
|
|
{
|
|
case NQTE_EXPLOSION2: //happens with rogue.
|
|
bufferlen -= 2; //trim the colour
|
|
buffer[1] = TE_EXPLOSION;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (ignoreprotocol)
|
|
{
|
|
ignoreprotocol=false;
|
|
bufferlen = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cldest)
|
|
{
|
|
if (!requireextension || cldest->fteprotocolextensions & requireextension)
|
|
#ifdef NQPROT
|
|
if (bufferlen && !cldest->nqprot)
|
|
#else
|
|
if (bufferlen)
|
|
#endif
|
|
{
|
|
ClientReliableCheckBlock(cldest, bufferlen);
|
|
ClientReliableWrite_SZ(cldest, buffer, bufferlen);
|
|
}
|
|
cldest = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (multicastpos && (writedest == &sv.datagram || writedest == &sv.multicast))
|
|
writedest = &sv.multicast;
|
|
else
|
|
multicastpos = 0;
|
|
if (bufferlen)
|
|
SZ_Write(writedest, buffer, bufferlen);
|
|
|
|
if (multicastpos)
|
|
{
|
|
vec3_t org;
|
|
org[0] = (*(short*)&buffer[multicastpos])/8.0f;
|
|
org[1] = (*(short*)&buffer[multicastpos+2])/8.0f;
|
|
org[2] = (*(short*)&buffer[multicastpos+4])/8.0f;
|
|
SV_MulticastProtExt(org, multicasttype, FULLDIMENSIONMASK, requireextension, 0);
|
|
}
|
|
writedest = NULL;
|
|
}
|
|
bufferlen = 0;
|
|
protocollen=0;
|
|
multicastpos=0;
|
|
requireextension=0;
|
|
}
|
|
void NPP_CheckFlush(void)
|
|
{
|
|
if (bufferlen >= protocollen && protocollen)
|
|
NPP_Flush();
|
|
}
|
|
|
|
void NPP_CheckDest(int dest)
|
|
{
|
|
if (dest == MSG_ONE)
|
|
{
|
|
client_t *cl = Write_GetClient();
|
|
if (!cl)
|
|
{
|
|
Con_Printf("Not a client\n");
|
|
return;
|
|
}
|
|
if ((cldest && cldest != cl) || writedest)
|
|
{
|
|
Con_Printf("MSG destination changed in the middle of a packet %i.\n", (int)*buffer);
|
|
NPP_Flush();
|
|
}
|
|
cldest = cl;
|
|
}
|
|
else
|
|
{
|
|
sizebuf_t *ndest = WriteDest(dest);
|
|
if (cldest || (writedest && writedest != ndest))
|
|
{
|
|
Con_Printf("MSG destination changed in the middle of a packet %i.\n", (int)*buffer);
|
|
NPP_Flush();
|
|
}
|
|
writedest = ndest;
|
|
}
|
|
}
|
|
void NPP_AddData(void *data, int len)
|
|
{
|
|
if (bufferlen+len > sizeof(buffer))
|
|
Sys_Error("Preparse buffer was filled\n");
|
|
memcpy(buffer+bufferlen, data, len);
|
|
bufferlen+=len;
|
|
}
|
|
|
|
void NPP_NQWriteByte(int dest, qbyte data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_CheckDest(dest);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(qbyte));
|
|
ClientReliableWrite_Byte(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteByte (NQWriteDest(dest), data);
|
|
#endif
|
|
if (!bufferlen) //new message section
|
|
{
|
|
switch(data)
|
|
{
|
|
case svc_temp_entity:
|
|
break;
|
|
case svc_setangle:
|
|
protocollen = sizeof(qbyte)*4;
|
|
break;
|
|
case svc_setview:
|
|
protocollen = sizeof(qbyte)*1 + sizeof(short);
|
|
break;
|
|
case svc_setname:
|
|
break;
|
|
case svc_setfrags:
|
|
protocollen = 4; //or this
|
|
break;
|
|
case svc_updatecolors:
|
|
protocollen = 3; //or even this
|
|
break;
|
|
case svc_print:
|
|
protocollen = 3;
|
|
break;
|
|
case svc_cdtrack:
|
|
protocollen = sizeof(qbyte)*3;
|
|
break;
|
|
case svc_killedmonster:
|
|
protocollen = 1;
|
|
break;
|
|
case svc_foundsecret:
|
|
protocollen = 1;
|
|
break;
|
|
case svc_intermission:
|
|
if (progstype == PROG_H2)
|
|
protocollen = 2;
|
|
else
|
|
protocollen = 1;
|
|
break;
|
|
case svc_finale:
|
|
protocollen = 2;
|
|
break;
|
|
case svcdp_skybox:
|
|
protocollen = 2;//it's just a string
|
|
break;
|
|
case svc_updatestat: //insta fixup
|
|
data = svc_updatestatlong; //ho hum... let it through (should check size later.)
|
|
protocollen = 5;
|
|
break;
|
|
case svc_stufftext:
|
|
case svc_centerprint:
|
|
break;
|
|
case svc_clearviewflags:
|
|
protocollen = 2;
|
|
ignoreprotocol = true;
|
|
break;
|
|
case svc_cutscene:
|
|
ignoreprotocol = true;
|
|
break;
|
|
default:
|
|
Con_Printf("bad protocol %i\n", (int)data);
|
|
protocollen = sizeof(buffer);
|
|
break;
|
|
}
|
|
majortype = data;
|
|
}
|
|
if (bufferlen == 1 && !protocollen) //some of them depend on the following bytes for size.
|
|
{
|
|
switch(majortype)
|
|
{
|
|
case svc_temp_entity:
|
|
switch(data)
|
|
{
|
|
case NQTE_BEAM:
|
|
data = TE_LIGHTNING1; //QW doesn't do te_beam. Replace with lightning1.
|
|
//fallthrough
|
|
case TE_LIGHTNING1:
|
|
case TE_LIGHTNING2:
|
|
case TE_LIGHTNING3:
|
|
multicastpos=4;
|
|
multicasttype=MULTICAST_PHS;
|
|
protocollen = sizeof(short)*6+sizeof(short)+sizeof(qbyte)*2;
|
|
break;
|
|
case TE_GUNSHOT:
|
|
multicastpos=3;
|
|
multicasttype=MULTICAST_PVS;
|
|
//we need to emit annother qbyte here. QuakeWorld has a number of particles.
|
|
//emit it here and we don't need to remember to play with temp_entity later
|
|
NPP_AddData(&data, sizeof(qbyte));
|
|
data = 1;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*3;
|
|
break;
|
|
case TE_EXPLOSION:
|
|
case TE_SPIKE:
|
|
case TE_SUPERSPIKE:
|
|
multicastpos=2;
|
|
multicasttype=MULTICAST_PHS_R;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*2;
|
|
break;
|
|
case TE_TAREXPLOSION:
|
|
case TE_WIZSPIKE:
|
|
case TE_KNIGHTSPIKE:
|
|
case TE_LAVASPLASH:
|
|
case TE_TELEPORT:
|
|
multicastpos=2;
|
|
multicasttype=MULTICAST_PVS;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*2;
|
|
break;
|
|
case TE_EXPLOSION3_NEH:
|
|
protocollen = sizeof(qbyte) + sizeof(short)*6;
|
|
ignoreprotocol = true;
|
|
break;
|
|
case NQTE_EXPLOSION2:
|
|
protocollen = sizeof(qbyte)*4 + sizeof(short)*3;
|
|
multicastpos=2;
|
|
multicasttype=MULTICAST_PHS_R;
|
|
break;
|
|
case TE_EXPLOSIONSMALL2:
|
|
data = TE_EXPLOSION;
|
|
protocollen = sizeof(qbyte)*2 + sizeof(short)*3;
|
|
multicastpos=2;
|
|
multicasttype=MULTICAST_PHS;
|
|
break;
|
|
case TE_RAILTRAIL:
|
|
protocollen = sizeof(short)*6+sizeof(qbyte)*1;
|
|
multicastpos=1;
|
|
multicasttype=MULTICAST_PHS;
|
|
break;
|
|
case TE_STREAM_CHAIN:
|
|
case TE_STREAM_SUNSTAFF1:
|
|
case TE_STREAM_SUNSTAFF2:
|
|
case TE_STREAM_LIGHTNING:
|
|
case TE_STREAM_ICECHUNKS:
|
|
case TE_STREAM_GAZE:
|
|
case TE_STREAM_FAMINE:
|
|
protocollen = sizeof(short)*(6+1)+sizeof(qbyte)*(2+2);
|
|
multicastpos = 8;
|
|
multicasttype=MULTICAST_PHS;
|
|
break;
|
|
case TE_STREAM_COLORBEAM:
|
|
protocollen = sizeof(short)*(6+1)+sizeof(qbyte)*(3+2);
|
|
multicastpos = 8;
|
|
multicasttype=MULTICAST_PHS;
|
|
break;
|
|
|
|
default:
|
|
protocollen = sizeof(buffer);
|
|
Con_Printf("bad tempentity\n");
|
|
break;
|
|
}
|
|
break;
|
|
case svc_setname:
|
|
case svc_stufftext:
|
|
case svc_centerprint:
|
|
break;
|
|
default:
|
|
Con_Printf("Non-Implemented svc\n");
|
|
protocollen = sizeof(buffer);
|
|
break;
|
|
}
|
|
}
|
|
if (!protocollen) //these protocols take strings, and are thus dynamically sized.
|
|
{
|
|
switch(majortype)
|
|
{
|
|
case svc_setname:
|
|
case svc_stufftext:
|
|
case svc_centerprint:
|
|
if (!data)
|
|
protocollen = bufferlen;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPP_AddData(&data, sizeof(qbyte));
|
|
NPP_CheckFlush();
|
|
}
|
|
|
|
void NPP_NQWriteChar(int dest, char data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_NQWriteByte(dest, (qbyte)data);
|
|
return;
|
|
/*
|
|
NPP_CheckDest(dest);
|
|
if (!bufferlen)
|
|
{
|
|
NPP_NQWriteByte(dest, (qbyte)data);
|
|
return;
|
|
}
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(char));
|
|
ClientReliableWrite_Char(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteChar (NQWriteDest(dest), data);
|
|
#endif
|
|
|
|
NPP_AddData(&data, sizeof(char));
|
|
NPP_CheckFlush();*/
|
|
}
|
|
|
|
void NPP_NQWriteShort(int dest, short data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_CheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte\n");
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(short));
|
|
ClientReliableWrite_Short(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteShort (NQWriteDest(dest), data);
|
|
#endif
|
|
|
|
data = LittleShort(data);
|
|
NPP_AddData(&data, sizeof(short));
|
|
NPP_CheckFlush();
|
|
}
|
|
|
|
void NPP_NQWriteLong(int dest, long data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_CheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte\n");
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(long));
|
|
ClientReliableWrite_Long(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteLong (NQWriteDest(dest), data);
|
|
#endif
|
|
|
|
data = LittleLong(data);
|
|
NPP_AddData(&data, sizeof(long));
|
|
NPP_CheckFlush();
|
|
}
|
|
void NPP_NQWriteAngle(int dest, float in) //replacement write func (nq to qw)
|
|
{
|
|
char data = (int)(in*256/360) & 255;
|
|
NPP_CheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte\n");
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE)
|
|
{
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(char));
|
|
ClientReliableWrite_Angle(cl, in);
|
|
}
|
|
}
|
|
else
|
|
MSG_WriteAngle (NQWriteDest(dest), in);
|
|
#endif
|
|
|
|
NPP_AddData(&data, sizeof(char));
|
|
NPP_CheckFlush();
|
|
}
|
|
void NPP_NQWriteCoord(int dest, float in) //replacement write func (nq to qw)
|
|
{
|
|
short data = (int)(in*8);
|
|
NPP_CheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte\n");
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(short));
|
|
ClientReliableWrite_Coord(cl, in);
|
|
}
|
|
} else
|
|
MSG_WriteCoord (NQWriteDest(dest), in);
|
|
#endif
|
|
|
|
data = LittleShort(data);
|
|
NPP_AddData(&data, sizeof(short));
|
|
NPP_CheckFlush();
|
|
}
|
|
void NPP_NQWriteString(int dest, char *data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_CheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte\n");
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, strlen(data)+1);
|
|
ClientReliableWrite_String(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteString (NQWriteDest(dest), data);
|
|
#endif
|
|
|
|
NPP_AddData(data, strlen(data)+1);
|
|
NPP_CheckFlush();
|
|
}
|
|
void NPP_NQWriteEntity(int dest, short data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_CheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte\n");
|
|
|
|
if (majortype == svc_temp_entity && data >= 0 && data <= sv.allocated_client_slots)
|
|
if (svs.clients[data-1].viewent)
|
|
data = svs.clients[data-1].viewent;
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(short));
|
|
ClientReliableWrite_Short(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteShort (NQWriteDest(dest), data);
|
|
#endif
|
|
|
|
NPP_AddData(&data, sizeof(short));
|
|
NPP_CheckFlush();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef NQPROT
|
|
|
|
|
|
//qw to nq translation is only useful if we allow nq clients to connect.
|
|
|
|
void NPP_QWFlush(void)
|
|
{
|
|
qbyte b;
|
|
if (!bufferlen)
|
|
return;
|
|
|
|
|
|
switch(majortype)
|
|
{
|
|
case svc_setname: //not a standard feature, but hey, if a progs wants bots.
|
|
bufferlen = 0;
|
|
NPP_SetInfo(&svs.clients[buffer[1]], "name", buffer+2);
|
|
break;
|
|
case svc_updatecolors:
|
|
bufferlen = 0;
|
|
NPP_SetInfo(&svs.clients[buffer[1]], "bottomcolor", va("%i", buffer[2]&15));
|
|
NPP_SetInfo(&svs.clients[buffer[1]], "topcolor", va("%i", buffer[2]/16));
|
|
break;
|
|
case svc_cdtrack:
|
|
if (bufferlen!=protocollen)
|
|
Con_Printf("svc_cdtrack wasn't the right length\n");
|
|
else
|
|
{
|
|
b = 0;
|
|
NPP_AddData(&b, sizeof(qbyte));
|
|
}
|
|
break;
|
|
//ignore these.
|
|
case svc_intermission:
|
|
if (writedest == &sv.reliable_datagram)
|
|
{
|
|
client_t *cl;
|
|
int i;
|
|
for (i = 0, cl = svs.clients; i < sv.allocated_client_slots; i++, cl++)
|
|
{
|
|
if (cl->state == cs_spawned && cl->nqprot)
|
|
{
|
|
vec3_t org, ang;
|
|
|
|
if (cl->zquake_extensions & Z_EXT_SERVERTIME)
|
|
{
|
|
ClientReliableCheckBlock(cl, 6);
|
|
ClientReliableWrite_Byte(cl, svc_updatestatlong);
|
|
ClientReliableWrite_Byte(cl, STAT_TIME);
|
|
ClientReliableWrite_Long(cl, (int)(sv.time * 1000));
|
|
cl->nextservertimeupdate = sv.time+10;
|
|
}
|
|
|
|
ClientReliableCheckBlock(cl, 1);
|
|
ClientReliableWrite_Byte(cl, svc_intermission);
|
|
|
|
org[0] = (*(short*)&buffer[1])/8.0f;
|
|
org[1] = (*(short*)&buffer[1+2])/8.0f;
|
|
org[2] = (*(short*)&buffer[1+4])/8.0f;
|
|
|
|
ang[0] = (*(qbyte*)&buffer[7])*360.0/255;
|
|
ang[1] = (*(qbyte*)&buffer[7+1])*360.0/255;
|
|
ang[2] = (*(qbyte*)&buffer[7+2])*360.0/255;
|
|
|
|
//move nq players to origin + angle
|
|
VectorCopy(cl->edict->v.origin, org);
|
|
VectorCopy(cl->edict->v.angles, ang);
|
|
}
|
|
}
|
|
}
|
|
bufferlen = 0;
|
|
protocollen=0;
|
|
writedest = NULL;
|
|
// case svc_finale:
|
|
// bufferlen = 0;
|
|
// break;
|
|
case svc_setview:
|
|
requireextension = PEXT_SETVIEW;
|
|
// bufferlen = 0;
|
|
break;
|
|
case svc_muzzleflash:
|
|
//we need to make a fake muzzleflash position.
|
|
multicastpos = 4;
|
|
{
|
|
short data;
|
|
float org[3];
|
|
edict_t *ent = EDICT_NUM(svprogfuncs, (*(short*)&buffer[1]));
|
|
VectorCopy(ent->v.origin, org);
|
|
|
|
data = LittleShort((short)(org[0]*8));
|
|
NPP_AddData(&data, sizeof(short));
|
|
data = LittleShort((short)(org[1]*8));
|
|
NPP_AddData(&data, sizeof(short));
|
|
data = LittleShort((short)(org[2]*8));
|
|
NPP_AddData(&data, sizeof(short));
|
|
|
|
}
|
|
bufferlen = 0;
|
|
break;
|
|
case svc_smallkick:
|
|
case svc_bigkick:
|
|
bufferlen = 0;
|
|
break;
|
|
case svc_updateuserinfo:
|
|
if (buffer[6])
|
|
{
|
|
Q_strncpyz(svs.clients[buffer[1]].userinfo, (buffer+6), sizeof(svs.clients[0].userinfo));
|
|
if (*Info_ValueForKey(svs.clients[buffer[1]].userinfo, "name"))
|
|
SV_ExtractFromUserinfo(&svs.clients[buffer[1]]);
|
|
else
|
|
*svs.clients[buffer[1]].name = '\0';
|
|
}
|
|
else
|
|
{
|
|
*svs.clients[buffer[1]].name = '\0';
|
|
*svs.clients[buffer[1]].userinfo = '\0';
|
|
}
|
|
|
|
break;
|
|
case svc_temp_entity:
|
|
switch(minortype)
|
|
{
|
|
case TE_LIGHTNINGBLOOD:
|
|
case TE_BLOOD: //needs to be converted to a particle
|
|
{
|
|
vec3_t org;
|
|
int v;
|
|
int i;
|
|
org[0] = (*(short*)&buffer[multicastpos])/8.0f;
|
|
org[1] = (*(short*)&buffer[multicastpos+2])/8.0f;
|
|
org[2] = (*(short*)&buffer[multicastpos+4])/8.0f;
|
|
|
|
MSG_WriteByte (&sv.nqmulticast, svc_particle);
|
|
MSG_WriteCoord (&sv.nqmulticast, org[0]);
|
|
MSG_WriteCoord (&sv.nqmulticast, org[1]);
|
|
MSG_WriteCoord (&sv.nqmulticast, org[2]);
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
v = 0*16;
|
|
if (v > 127)
|
|
v = 127;
|
|
else if (v < -128)
|
|
v = -128;
|
|
MSG_WriteChar (&sv.nqmulticast, v);
|
|
}
|
|
MSG_WriteByte (&sv.nqmulticast, buffer[2]*20);
|
|
if (minortype == TE_LIGHTNINGBLOOD)
|
|
MSG_WriteByte (&sv.nqmulticast, 225);
|
|
else
|
|
MSG_WriteByte (&sv.nqmulticast, 73);
|
|
}
|
|
bufferlen = 0;
|
|
break;
|
|
case TE_GUNSHOT: //needs qbyte 3 removed
|
|
memmove(buffer+2, buffer+3, bufferlen-3);
|
|
bufferlen--;
|
|
break;
|
|
}
|
|
}
|
|
if (ignoreprotocol)
|
|
{
|
|
ignoreprotocol=false;
|
|
bufferlen = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cldest)
|
|
{
|
|
if (!requireextension || cldest->fteprotocolextensions & requireextension)
|
|
if (bufferlen && cldest->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cldest, bufferlen);
|
|
ClientReliableWrite_SZ(cldest, buffer, bufferlen);
|
|
}
|
|
cldest = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (multicastpos && (writedest == &sv.nqdatagram || writedest == &sv.nqmulticast))
|
|
writedest = &sv.nqmulticast;
|
|
else
|
|
multicastpos = 0;
|
|
if (bufferlen)
|
|
SZ_Write(writedest, buffer, bufferlen);
|
|
|
|
if (multicastpos)
|
|
{
|
|
vec3_t org;
|
|
org[0] = (*(short*)&buffer[multicastpos])/8.0f;
|
|
org[1] = (*(short*)&buffer[multicastpos+2])/8.0f;
|
|
org[2] = (*(short*)&buffer[multicastpos+4])/8.0f;
|
|
SV_MulticastProtExt(org, multicasttype, FULLDIMENSIONMASK, requireextension, 0);
|
|
}
|
|
writedest = NULL;
|
|
}
|
|
bufferlen = 0;
|
|
nullterms=0;
|
|
protocollen=0;
|
|
multicastpos=0;
|
|
requireextension=0;
|
|
}
|
|
void NPP_QWCheckFlush(void)
|
|
{
|
|
if (bufferlen >= protocollen && protocollen && !nullterms)
|
|
NPP_QWFlush();
|
|
}
|
|
|
|
void NPP_QWCheckDest(int dest)
|
|
{
|
|
if (dest == MSG_ONE)
|
|
{
|
|
client_t *cl = Write_GetClient();
|
|
if (!cl)
|
|
{
|
|
Con_Printf("Not a client\n");
|
|
return;
|
|
}
|
|
if ((cldest && cldest != cl) || writedest)
|
|
{
|
|
Con_Printf("MSG destination changed in the middle of a packet %i.\n", (int)*buffer);
|
|
NPP_QWFlush();
|
|
}
|
|
cldest = cl;
|
|
}
|
|
else
|
|
{
|
|
sizebuf_t *ndest = NQWriteDest(dest);
|
|
if (cldest || (writedest && writedest != ndest))
|
|
{
|
|
Con_Printf("MSG destination changed in the middle of a packet %i.\n", (int)*buffer);
|
|
NPP_QWFlush();
|
|
}
|
|
writedest = ndest;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void NPP_QWWriteByte(int dest, qbyte data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_QWCheckDest(dest);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && !cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(qbyte));
|
|
ClientReliableWrite_Byte(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteByte (WriteDest(dest), data);
|
|
#endif
|
|
if (!bufferlen) //new message section
|
|
{
|
|
switch(data)
|
|
{
|
|
case svc_temp_entity:
|
|
break;
|
|
case svc_setangle:
|
|
protocollen = sizeof(qbyte)*4;
|
|
break;
|
|
case svc_setview:
|
|
protocollen = sizeof(qbyte)*1 + sizeof(short);
|
|
break;
|
|
case svc_cdtrack:
|
|
protocollen = sizeof(qbyte)*2;
|
|
break;
|
|
case svc_killedmonster:
|
|
protocollen = 1;
|
|
break;
|
|
case svc_foundsecret:
|
|
protocollen = 1;
|
|
break;
|
|
case svc_intermission:
|
|
protocollen = 10;
|
|
break;
|
|
case svc_finale:
|
|
protocollen = 2;
|
|
break;
|
|
case svc_muzzleflash:
|
|
protocollen = 3;
|
|
break;
|
|
case svc_smallkick:
|
|
case svc_bigkick:
|
|
protocollen = 1;
|
|
break;
|
|
case svc_print:
|
|
protocollen = 2;
|
|
nullterms=1;
|
|
break;
|
|
case svc_centerprint:
|
|
case svc_stufftext:
|
|
protocollen = 1;
|
|
nullterms=1;
|
|
break;
|
|
case svc_updatestat:
|
|
protocollen = 3;
|
|
break;
|
|
case svc_updateping:
|
|
case svc_updatefrags:
|
|
protocollen = 4;
|
|
break;
|
|
case svc_updateentertime:
|
|
protocollen = 6;
|
|
break;
|
|
case svc_updateuserinfo:
|
|
protocollen = 6;
|
|
nullterms = 1;
|
|
break;
|
|
case svc_updatestatlong:
|
|
protocollen = 6;
|
|
break;
|
|
default:
|
|
Con_Printf("bad protocol %i\n", (int)data);
|
|
protocollen = sizeof(buffer);
|
|
break;
|
|
}
|
|
majortype = data;
|
|
}
|
|
if (bufferlen == 1 && !protocollen) //some of them depend on the following bytes for size.
|
|
{
|
|
switch(majortype)
|
|
{
|
|
case svc_temp_entity:
|
|
minortype = data;
|
|
switch(data)
|
|
{
|
|
case TE_LIGHTNING1:
|
|
case TE_LIGHTNING2:
|
|
case TE_LIGHTNING3:
|
|
multicastpos=4;
|
|
multicasttype=MULTICAST_PHS;
|
|
protocollen = sizeof(short)*6+sizeof(short)+sizeof(qbyte)*2;
|
|
break;
|
|
case TE_BLOOD: //needs to be converted to a particle
|
|
case TE_GUNSHOT: //needs qbyte 2 removed
|
|
multicastpos=3;
|
|
multicasttype=MULTICAST_PVS;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*3;
|
|
break;
|
|
case TE_LIGHTNINGBLOOD:
|
|
case TE_EXPLOSION:
|
|
case TE_SPIKE:
|
|
case TE_SUPERSPIKE:
|
|
multicastpos=2;
|
|
multicasttype=MULTICAST_PHS_R;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*2;
|
|
break;
|
|
case TE_TAREXPLOSION:
|
|
case TE_WIZSPIKE:
|
|
case TE_KNIGHTSPIKE:
|
|
case TE_LAVASPLASH:
|
|
case TE_TELEPORT:
|
|
multicastpos=2;
|
|
multicasttype=MULTICAST_PVS;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*2;
|
|
break;
|
|
case TE_RAILTRAIL:
|
|
multicastpos=1;
|
|
multicasttype=MULTICAST_PVS;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*1;
|
|
break;
|
|
default:
|
|
protocollen = sizeof(buffer);
|
|
Con_Printf("bad tempentity - %i\n", data);
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
Con_Printf("Non-Implemented svc\n");
|
|
protocollen = sizeof(buffer);
|
|
break;
|
|
}
|
|
}
|
|
|
|
NPP_AddData(&data, sizeof(qbyte));
|
|
if (!data && bufferlen>=protocollen)
|
|
if (nullterms)
|
|
nullterms--;
|
|
NPP_QWCheckFlush();
|
|
}
|
|
|
|
void NPP_QWWriteChar(int dest, char data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_QWCheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte (last was %i)\n", majortype);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && !cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(char));
|
|
ClientReliableWrite_Char(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteChar (WriteDest(dest), data);
|
|
#endif
|
|
|
|
NPP_AddData(&data, sizeof(char));
|
|
if (!data && bufferlen>=protocollen)
|
|
if (nullterms)
|
|
nullterms--;
|
|
NPP_QWCheckFlush();
|
|
}
|
|
|
|
void NPP_QWWriteShort(int dest, short data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_QWCheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte (last was %i)\n", majortype);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && !cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(short));
|
|
ClientReliableWrite_Short(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteShort (WriteDest(dest), data);
|
|
#endif
|
|
|
|
data = LittleShort(data);
|
|
NPP_AddData(&data, sizeof(short));
|
|
NPP_QWCheckFlush();
|
|
}
|
|
|
|
void NPP_QWWriteLong(int dest, long data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_QWCheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte (last was %i)\n", majortype);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && !cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(long));
|
|
ClientReliableWrite_Long(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteLong (WriteDest(dest), data);
|
|
#endif
|
|
|
|
data = LittleLong(data);
|
|
NPP_AddData(&data, sizeof(long));
|
|
NPP_QWCheckFlush();
|
|
}
|
|
void NPP_QWWriteAngle(int dest, float in) //replacement write func (nq to qw)
|
|
{
|
|
char data = (int)(in*256/360) & 255;
|
|
NPP_QWCheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte (last was %i)\n", majortype);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && !cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(char));
|
|
ClientReliableWrite_Angle(cl, in);
|
|
}
|
|
} else
|
|
MSG_WriteAngle (WriteDest(dest), in);
|
|
#endif
|
|
|
|
NPP_AddData(&data, sizeof(char));
|
|
NPP_QWCheckFlush();
|
|
}
|
|
void NPP_QWWriteCoord(int dest, float in) //replacement write func (nq to qw)
|
|
{
|
|
short data = (int)(in*8);
|
|
NPP_QWCheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte (last was %i)\n", majortype);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && !cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(short));
|
|
ClientReliableWrite_Coord(cl, in);
|
|
}
|
|
} else
|
|
MSG_WriteCoord (WriteDest(dest), in);
|
|
#endif
|
|
|
|
data = LittleShort(data);
|
|
NPP_AddData(&data, sizeof(short));
|
|
NPP_QWCheckFlush();
|
|
}
|
|
void NPP_QWWriteString(int dest, char *data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_QWCheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte (last was %i)\n", majortype);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && !cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, strlen(data)+1);
|
|
ClientReliableWrite_String(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteString (WriteDest(dest), data);
|
|
#endif
|
|
|
|
NPP_AddData(data, strlen(data)+1);
|
|
if (nullterms)
|
|
nullterms--;
|
|
NPP_QWCheckFlush();
|
|
}
|
|
void NPP_QWWriteEntity(int dest, short data) //replacement write func (nq to qw)
|
|
{
|
|
NPP_QWCheckDest(dest);
|
|
if (!bufferlen)
|
|
Con_Printf("Messages should start with WriteByte (last was %i)\n", majortype);
|
|
|
|
#ifdef NQPROT
|
|
if (dest == MSG_ONE) {
|
|
client_t *cl = Write_GetClient();
|
|
if (cl && !cl->nqprot)
|
|
{
|
|
ClientReliableCheckBlock(cl, sizeof(short));
|
|
ClientReliableWrite_Short(cl, data);
|
|
}
|
|
} else
|
|
MSG_WriteShort (WriteDest(dest), data);
|
|
#endif
|
|
|
|
NPP_AddData(&data, sizeof(short));
|
|
NPP_QWCheckFlush();
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define DF_ORIGIN 1
|
|
#define DF_ANGLES (1<<3)
|
|
#define DF_EFFECTS (1<<6)
|
|
#define DF_SKINNUM (1<<7)
|
|
#define DF_DEAD (1<<8)
|
|
#define DF_GIB (1<<9)
|
|
#define DF_WEAPONFRAME (1<<10)
|
|
#define DF_MODEL (1<<11)
|
|
|
|
#define PF_MSEC (1<<0)
|
|
#define PF_COMMAND (1<<1)
|
|
#define PF_VELOCITY1 (1<<2)
|
|
#define PF_VELOCITY2 (1<<3)
|
|
#define PF_VELOCITY3 (1<<4)
|
|
#define PF_MODEL (1<<5)
|
|
#define PF_SKINNUM (1<<6)
|
|
#define PF_EFFECTS (1<<7)
|
|
#define PF_WEAPONFRAME (1<<8) // only sent for view player
|
|
#define PF_DEAD (1<<9) // don't block movement any more
|
|
#define PF_GIB (1<<10) // offset the view height differently
|
|
|
|
|
|
|
|
|
|
int sv_demo_spikeindex;
|
|
|
|
|
|
|
|
|
|
void NPP_MVDFlush(void)
|
|
{
|
|
if (!bufferlen)
|
|
return;
|
|
|
|
switch(majortype)
|
|
{
|
|
case svc_spawnbaseline:
|
|
SV_FlushDemoSignon();
|
|
cldest = NULL;
|
|
writedest = &sv.demosignon;
|
|
|
|
if (1)
|
|
{
|
|
int entnum, i;
|
|
entity_state_t *ent;
|
|
|
|
if (!sv.demostate)
|
|
sv.demostate = BZ_Malloc(sizeof(entity_state_t)*MAX_EDICTS);
|
|
|
|
sv.demostatevalid = true;
|
|
entnum = buffer[1] + (buffer[2]<<8);
|
|
// if (entnum < MAX_CLIENTS)
|
|
// break;
|
|
ent = &sv.demostate[entnum];
|
|
|
|
ent->number = entnum;
|
|
ent->modelindex = buffer[3];
|
|
ent->frame = buffer[4];
|
|
ent->colormap = buffer[5];
|
|
ent->skinnum = buffer[6];
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
{
|
|
ent->origin[i] = (short)(buffer[7+i*3] + (buffer[8+i*3]<<8))/8.0f;
|
|
ent->angles[i] = buffer[9+i*3]*360.0/256;
|
|
}
|
|
}
|
|
break;
|
|
case svc_spawnstatic:
|
|
SV_FlushDemoSignon();
|
|
cldest = NULL;
|
|
writedest = &sv.demosignon;
|
|
break;
|
|
case svc_modellist:
|
|
ignoreprotocol=true;
|
|
{
|
|
int i;
|
|
int rpos, s;
|
|
i = buffer[1];
|
|
rpos = 2;
|
|
while (1)
|
|
{
|
|
i++;
|
|
s = rpos;
|
|
while(buffer[rpos])
|
|
rpos++;
|
|
|
|
if (rpos == s) //end
|
|
break;
|
|
|
|
strcpy(sv.demmodel_precache[i], buffer+s);
|
|
if (!strcmp(sv.demmodel_precache[i], "progs/spike.mdl"))
|
|
sv_demo_spikeindex = i;
|
|
rpos++;
|
|
}
|
|
}
|
|
break;
|
|
case svc_soundlist:
|
|
ignoreprotocol=true;
|
|
{
|
|
int i;
|
|
int rpos, s;
|
|
i = buffer[1];
|
|
rpos = 2;
|
|
while (1)
|
|
{
|
|
i++;
|
|
s = rpos;
|
|
while(buffer[rpos])
|
|
rpos++;
|
|
|
|
if (rpos == s) //end
|
|
break;
|
|
|
|
strcpy(sv.demsound_precache[i], buffer+s);
|
|
rpos++;
|
|
}
|
|
}
|
|
break;
|
|
case svc_serverdata:
|
|
{
|
|
int i;
|
|
sv_demo_spikeindex = 0; //new map, new precaches.
|
|
|
|
i = 9;
|
|
strcpy(sv.demgamedir, buffer+i);
|
|
for(;i < bufferlen && buffer[i];i++)
|
|
;
|
|
i++;
|
|
i+=4;
|
|
Q_strncpyz(sv.demfullmapname, buffer+i, sizeof(sv.demfullmapname));
|
|
for(;i < bufferlen && buffer[i];i++)
|
|
;
|
|
i+=4*10;
|
|
}
|
|
ignoreprotocol=true;
|
|
break;
|
|
case svc_lightstyle:
|
|
sv.demolightstyles[buffer[1]] = Hunk_Alloc(strlen(buffer+2)+1);
|
|
strcpy(sv.demolightstyles[buffer[1]], buffer+2);
|
|
break;
|
|
|
|
case svc_updatestat:
|
|
case svc_updatestatlong: //make sure we update the running players stats properly.
|
|
{
|
|
int v, s;
|
|
if (majortype == svc_updatestat)
|
|
v = buffer[2];
|
|
else
|
|
v = buffer[2] | (buffer[3]<<8) | (buffer[4]<<16) | (buffer[5]<<24);
|
|
s = buffer[1];
|
|
|
|
sv.recordedplayer[sv.lastto].stats[s] = v;
|
|
|
|
ignoreprotocol=true;
|
|
}
|
|
break;
|
|
case svc_packetentities:
|
|
case svc_deltapacketentities: //read the delta in to the array.
|
|
ignoreprotocol=true; //a bug exists in that the delta MUST have been reliably recorded.
|
|
{
|
|
int i;
|
|
entity_state_t *ents;
|
|
unsigned short s;
|
|
if (!sv.demostate)
|
|
sv.demostate = BZ_Malloc(sizeof(entity_state_t)*MAX_EDICTS);
|
|
sv.demostatevalid = true;
|
|
i = majortype-svc_packetentities+1;
|
|
while (1)
|
|
{
|
|
s = buffer[i] + buffer[i+1]*256;
|
|
i+=2;
|
|
if (!s)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
ents = &sv.demostate[s&511];
|
|
ents->number = s&511;
|
|
s &= ~511;
|
|
if (s & U_REMOVE)
|
|
{
|
|
ents->modelindex = 0;
|
|
}
|
|
|
|
if (s & U_MOREBITS)
|
|
{
|
|
s |= buffer[i];
|
|
i++;
|
|
}
|
|
|
|
if (s & U_MODEL)
|
|
{
|
|
ents->modelindex = buffer[i];
|
|
i++;
|
|
}
|
|
|
|
if (s & U_FRAME)
|
|
{
|
|
ents->frame = buffer[i];
|
|
i++;
|
|
}
|
|
|
|
if (s & U_COLORMAP)
|
|
{
|
|
ents->colormap = buffer[i];
|
|
i++;
|
|
}
|
|
|
|
if (s & U_SKIN)
|
|
{
|
|
ents->skinnum = buffer[i];
|
|
i++;
|
|
}
|
|
|
|
if (s & U_EFFECTS)
|
|
{
|
|
ents->effects = buffer[i];
|
|
i++;
|
|
}
|
|
|
|
if (s & U_ORIGIN1)
|
|
{
|
|
ents->origin[0] = (short)(buffer[i]+buffer[i+1]*256) /8.0f;
|
|
i+=2;
|
|
}
|
|
|
|
if (s & U_ANGLE1)
|
|
{
|
|
ents->angles[0] = (unsigned char)(buffer[i]) * (360.0/256);
|
|
i++;
|
|
}
|
|
|
|
if (s & U_ORIGIN2)
|
|
{
|
|
ents->origin[1] = (short)(buffer[i]+buffer[i+1]*256) /8.0f;
|
|
i+=2;
|
|
}
|
|
|
|
if (s & U_ANGLE2)
|
|
{
|
|
ents->angles[1] = (unsigned char)(buffer[i]) * (360.0/256);
|
|
i++;
|
|
}
|
|
|
|
if (s & U_ORIGIN3)
|
|
{
|
|
ents->origin[2] = (short)(buffer[i]+buffer[i+1]*256) /8.0f;
|
|
i+=2;
|
|
}
|
|
|
|
if (s & U_ANGLE3)
|
|
{
|
|
ents->angles[2] = (unsigned char)(buffer[i]) * (360.0/256);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svc_playerinfo:
|
|
ignoreprotocol=true;
|
|
{
|
|
int i, j;
|
|
unsigned short flags;
|
|
entity_state_t *ents;
|
|
int wframe, playernum;
|
|
vec3_t oldorg;
|
|
vec3_t oldang;
|
|
|
|
if (!sv.demostate)
|
|
sv.demostate = BZ_Malloc(sizeof(entity_state_t)*MAX_EDICTS);
|
|
|
|
sv.demostatevalid = true;
|
|
|
|
flags = buffer[2] + buffer[3]*256;
|
|
|
|
playernum = buffer[1];
|
|
ents = &sv.demostate[playernum+1];
|
|
ents->frame = buffer[4];
|
|
|
|
// ents->colormap=playernum+1;
|
|
|
|
VectorCopy(ents->origin, oldorg);
|
|
i = 5;
|
|
for (j=0 ; j<3 ; j++)
|
|
if (flags & (DF_ORIGIN << j))
|
|
{
|
|
ents->origin[j] = (int)(buffer[i] + (buffer[i+1]<<8))/8.0f;
|
|
i+=2;
|
|
}
|
|
VectorSubtract(ents->origin, oldorg, sv.recordedplayer[playernum].velocity);
|
|
VectorScale(sv.recordedplayer[playernum].velocity, realtime - sv.recordedplayer[playernum].updatetime, sv.recordedplayer[playernum].velocity);
|
|
|
|
VectorCopy(ents->angles, oldang);
|
|
for (j=0 ; j<3 ; j++)
|
|
if (flags & (DF_ANGLES << j))
|
|
{
|
|
ents->angles[j] = (int)(buffer[i] + (buffer[i+1]<<8))*360.0f/(256*256);
|
|
i+=2;
|
|
}
|
|
|
|
if (flags & (DF_ANGLES << 0)) //stupid quake bug I believe is the correct quote...
|
|
ents->angles[0] = ents->angles[0]*-1/3.0f; //also scale pitch down as well as invert
|
|
|
|
VectorSubtract(ents->angles, oldang, sv.recordedplayer[playernum].avelocity);
|
|
VectorScale(sv.recordedplayer[playernum].avelocity, realtime - sv.recordedplayer[playernum].updatetime, sv.recordedplayer[playernum].avelocity);
|
|
|
|
if (flags & DF_MODEL)
|
|
{
|
|
ents->modelindex = buffer[i];
|
|
i+=1;
|
|
}
|
|
if (flags & DF_SKINNUM)
|
|
{
|
|
ents->skinnum = buffer[i];
|
|
i+=1;
|
|
}
|
|
if (flags & DF_EFFECTS)
|
|
{
|
|
ents->effects = buffer[i];
|
|
i+=1;
|
|
}
|
|
if (flags & DF_WEAPONFRAME)
|
|
{
|
|
wframe = buffer[i];
|
|
i+=1;
|
|
}
|
|
else wframe = 0;
|
|
|
|
sv.recordedplayer[playernum].weaponframe = wframe;
|
|
sv.recordedplayer[playernum].updatetime = realtime;
|
|
|
|
ignoreprotocol=true;
|
|
}
|
|
break;
|
|
|
|
case svc_nails:
|
|
case svc_nails2:
|
|
sv.numdemospikes = buffer[1];
|
|
{
|
|
qboolean hasid = (majortype==svc_nails2);
|
|
char *bits;
|
|
int i;
|
|
bits = buffer+2;
|
|
for (i = 0; i < sv.numdemospikes; i++)
|
|
{
|
|
if (hasid)
|
|
{
|
|
sv.demospikes[i].id = *bits;
|
|
bits++;
|
|
}
|
|
else
|
|
sv.demospikes[i].id = 0;
|
|
sv.demospikes[i].modelindex = sv_demo_spikeindex;
|
|
sv.demospikes[i].org[0] = ( ( bits[0] + ((bits[1]&15)<<8) ) <<1) - 4096;
|
|
sv.demospikes[i].org[1] = ( ( (bits[1]>>4) + (bits[2]<<4) ) <<1) - 4096;
|
|
sv.demospikes[i].org[2] = ( ( bits[3] + ((bits[4]&15)<<8) ) <<1) - 4096;
|
|
sv.demospikes[i].pitch = (bits[4]>>4);
|
|
sv.demospikes[i].yaw = bits[5];
|
|
|
|
bits+=6;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case svc_stufftext:
|
|
ignoreprotocol = true;
|
|
Cmd_TokenizeString(buffer+1);
|
|
if (!stricmp(Cmd_Argv(0), "fullserverinfo"))
|
|
{
|
|
Q_strncpyz(sv.demoinfo, Cmd_Argv(1), sizeof(sv.demoinfo));
|
|
break;
|
|
}
|
|
break;
|
|
case svc_updateping:
|
|
{
|
|
int j;
|
|
j = buffer[1];
|
|
sv.recordedplayer[j].ping = buffer[2] | (buffer[3]<<8);
|
|
}
|
|
break;
|
|
case svc_updatepl:
|
|
{
|
|
int j;
|
|
j = buffer[1];
|
|
sv.recordedplayer[j].pl = buffer[2] | (buffer[3]<<8);
|
|
}
|
|
break;
|
|
case svc_updatefrags:
|
|
{
|
|
int j;
|
|
j = buffer[1];
|
|
sv.recordedplayer[j].frags = buffer[2] | (buffer[3]<<8);
|
|
}
|
|
break;
|
|
case svc_setinfo:
|
|
// ignoreprotocol = true;
|
|
{
|
|
int j;
|
|
j = buffer[1];
|
|
Info_SetValueForStarKey(sv.recordedplayer[j].userinfo, buffer+2, buffer+2+strlen(buffer+2)+1, sizeof(sv.recordedplayer[j].userinfo));
|
|
}
|
|
break;
|
|
case svc_updateuserinfo:
|
|
// ignoreprotocol = true;
|
|
{
|
|
int j;
|
|
j = buffer[1];
|
|
sv.recordedplayer[j].userid = buffer[2] | (buffer[3]<<8) | (buffer[4]<<16) | (buffer[5]<<24);
|
|
Q_strncpyz(sv.recordedplayer[j].userinfo, buffer+6, sizeof(sv.recordedplayer[j].userinfo));
|
|
}
|
|
break;
|
|
case svc_setangle: //FIXME: forward on to trackers.
|
|
ignoreprotocol = true;
|
|
break;
|
|
|
|
case svc_disconnect:
|
|
svs.spawncount++;
|
|
SV_BroadcastCommand("changing\n");
|
|
SV_BroadcastCommand ("reconnect\n");
|
|
ignoreprotocol = true;
|
|
break;
|
|
}
|
|
if (ignoreprotocol)
|
|
{
|
|
ignoreprotocol=false;
|
|
bufferlen = 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cldest)
|
|
{
|
|
if (!requireextension || cldest->fteprotocolextensions & requireextension)
|
|
if (bufferlen)
|
|
{
|
|
ClientReliableCheckBlock(cldest, bufferlen);
|
|
ClientReliableWrite_SZ(cldest, buffer, bufferlen);
|
|
}
|
|
cldest = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (multicastpos && (writedest == &sv.datagram || writedest == &sv.multicast))
|
|
writedest = &sv.multicast;
|
|
else
|
|
multicastpos = 0;
|
|
if (bufferlen)
|
|
SZ_Write(writedest, buffer, bufferlen);
|
|
|
|
if (multicastpos)
|
|
{
|
|
vec3_t org;
|
|
org[0] = (*(short*)&buffer[multicastpos])/8.0f;
|
|
org[1] = (*(short*)&buffer[multicastpos+2])/8.0f;
|
|
org[2] = (*(short*)&buffer[multicastpos+4])/8.0f;
|
|
SV_MulticastProtExt(org, multicasttype, FULLDIMENSIONMASK, requireextension, 0);
|
|
}
|
|
writedest = NULL;
|
|
}
|
|
bufferlen = 0;
|
|
protocollen=0;
|
|
multicastpos=0;
|
|
requireextension=0;
|
|
}
|
|
void NPP_MVDForceFlush(void)
|
|
{
|
|
if (bufferlen)
|
|
{
|
|
Con_Printf("Forcing flush mvd->qw prot\n");
|
|
Con_Printf("(last was %i)\n", (int)majortype);
|
|
NPP_MVDFlush();
|
|
}
|
|
}
|
|
void NPP_MVDCheckFlush(void)
|
|
{
|
|
if (bufferlen >= protocollen && protocollen)
|
|
NPP_MVDFlush();
|
|
}
|
|
|
|
void NPP_MVDCheckDest(client_t *cl, int broadcast)
|
|
{
|
|
if (!broadcast)
|
|
{
|
|
if (!cl)
|
|
{
|
|
Con_Printf("Not a client\n");
|
|
return;
|
|
}
|
|
if ((cldest && cldest != cl) || writedest)
|
|
{
|
|
Con_Printf("MSG destination changed in the middle of a packet.\n");
|
|
NPP_MVDFlush();
|
|
}
|
|
cldest = cl;
|
|
}
|
|
else
|
|
{
|
|
sizebuf_t *ndest = &sv.reliable_datagram;
|
|
if (cldest || (writedest && writedest != ndest))
|
|
{
|
|
Con_Printf("MSG destination changed in the middle of a packet.\n");
|
|
NPP_MVDFlush();
|
|
}
|
|
writedest = ndest;
|
|
}
|
|
}
|
|
|
|
void NPP_MVDWriteByte(qbyte data, client_t *to, int broadcast) //replacement write func (nq to qw)
|
|
{
|
|
int i;
|
|
NPP_MVDCheckDest(to, broadcast);
|
|
|
|
if (!bufferlen) //new message section
|
|
{
|
|
switch(data)
|
|
{
|
|
case svc_temp_entity://depends on following bytes
|
|
break;
|
|
case svc_serverinfo:
|
|
case svc_print:
|
|
case svc_sound:
|
|
case svc_serverdata:
|
|
case svc_stufftext:
|
|
case svc_modellist:
|
|
case svc_soundlist:
|
|
case svc_updateuserinfo:
|
|
case svc_playerinfo:
|
|
case svc_packetentities:
|
|
case svc_deltapacketentities:
|
|
case svc_lightstyle:
|
|
case svc_nails:
|
|
case svc_centerprint:
|
|
case svc_setinfo:
|
|
break;
|
|
case svc_setangle:
|
|
if (sv.mvdplayback)
|
|
protocollen = sizeof(qbyte)*5; //MVDSV writes an extra client num too.
|
|
else
|
|
protocollen = sizeof(qbyte)*4; //MVDSV writes an extra client num too.
|
|
break;
|
|
case svc_setview:
|
|
protocollen = sizeof(qbyte)*1 + sizeof(short);
|
|
break;
|
|
case svc_cdtrack:
|
|
protocollen = sizeof(qbyte)*2;
|
|
break;
|
|
case svc_killedmonster:
|
|
protocollen = 1;
|
|
break;
|
|
case svc_foundsecret:
|
|
protocollen = 1;
|
|
break;
|
|
// case svc_intermission:
|
|
// protocollen = 1;
|
|
// break;
|
|
// case svc_finale:
|
|
// protocollen = 2;
|
|
// break;
|
|
case svc_muzzleflash:
|
|
protocollen = 3;
|
|
break;
|
|
case svc_smallkick:
|
|
case svc_bigkick:
|
|
protocollen = 1;
|
|
break;
|
|
|
|
case svc_spawnstaticsound:
|
|
protocollen = 10;
|
|
break;
|
|
case svc_spawnstatic:
|
|
protocollen = 14;
|
|
break;
|
|
case svc_spawnbaseline:
|
|
protocollen = 16;
|
|
break;
|
|
case svc_updateping:
|
|
case svc_updatefrags:
|
|
protocollen = 4;
|
|
break;
|
|
case svc_updatestat:
|
|
case svc_updatepl:
|
|
protocollen = 3;
|
|
break;
|
|
case svc_updatestatlong:
|
|
protocollen = 6;
|
|
break;
|
|
case svc_updateentertime:
|
|
protocollen = 6;
|
|
break;
|
|
case svc_intermission:
|
|
protocollen = 10;
|
|
break;
|
|
case svc_disconnect:
|
|
protocollen = 2+strlen("EndOfDemo");
|
|
break;
|
|
case svc_chokecount:
|
|
protocollen = 2;
|
|
break;
|
|
case svc_damage:
|
|
protocollen = 9;
|
|
break;
|
|
default:
|
|
Con_Printf("bad protocol %i\n", (int)data);
|
|
Con_Printf("(last was %i)\n", (int)majortype);
|
|
protocollen = sizeof(buffer);
|
|
net_message.cursize=0;
|
|
data = svc_nop;
|
|
protocollen = 1;
|
|
break;
|
|
}
|
|
majortype = data;
|
|
}
|
|
else if (!protocollen)
|
|
{
|
|
switch(majortype)
|
|
{
|
|
case svc_temp_entity:
|
|
if (bufferlen == 1)
|
|
{
|
|
minortype = data;
|
|
switch(data)
|
|
{
|
|
case TE_LIGHTNING1:
|
|
case TE_LIGHTNING2:
|
|
case TE_LIGHTNING3:
|
|
multicastpos=4;
|
|
multicasttype=MULTICAST_PHS;
|
|
protocollen = sizeof(short)*6+sizeof(short)+sizeof(qbyte)*2;
|
|
break;
|
|
case TE_BLOOD: //needs to be converted to a particle
|
|
case TE_GUNSHOT: //needs qbyte 2 removed
|
|
multicastpos=3;
|
|
multicasttype=MULTICAST_PVS;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*3;
|
|
break;
|
|
case TE_LIGHTNINGBLOOD:
|
|
case TE_EXPLOSION:
|
|
case TE_SPIKE:
|
|
case TE_SUPERSPIKE:
|
|
multicastpos=2;
|
|
multicasttype=MULTICAST_PHS_R;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*2;
|
|
break;
|
|
case TE_TAREXPLOSION:
|
|
case TE_WIZSPIKE:
|
|
case TE_KNIGHTSPIKE:
|
|
case TE_LAVASPLASH:
|
|
case TE_TELEPORT:
|
|
multicastpos=2;
|
|
multicasttype=MULTICAST_PVS;
|
|
protocollen = sizeof(short)*3+sizeof(qbyte)*2;
|
|
break;
|
|
default:
|
|
protocollen = sizeof(buffer);
|
|
Con_Printf("bad tempentity\n");
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case svc_serverdata:
|
|
if (bufferlen > 9)
|
|
{
|
|
i = 9;
|
|
for(;i < bufferlen && buffer[i];i++)
|
|
;
|
|
i++;
|
|
i+=4;
|
|
for(;i < bufferlen && buffer[i];i++)
|
|
;
|
|
i+=4*10;
|
|
if (i <= bufferlen)
|
|
protocollen = i;
|
|
}
|
|
|
|
break;
|
|
case svc_stufftext:
|
|
if (!data) //terminated by a null term
|
|
protocollen = bufferlen;
|
|
break;
|
|
|
|
case svc_modellist:
|
|
case svc_soundlist:
|
|
if (!data)
|
|
if (!buffer[bufferlen-1]) //two null bytes marks the last string
|
|
protocollen = bufferlen+2;
|
|
break;
|
|
|
|
case svc_updateuserinfo: //6 bytes then a string
|
|
if (!data && bufferlen>=6)
|
|
protocollen = bufferlen+1;
|
|
break;
|
|
|
|
case svc_playerinfo:
|
|
if (bufferlen==4)
|
|
{
|
|
unsigned short pflags;
|
|
int j;
|
|
ignoreprotocol=true;
|
|
pflags = buffer[2] + (buffer[3]*256); //little endian
|
|
|
|
protocollen = 4+1;
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
if (pflags & (DF_ORIGIN << j))
|
|
protocollen += 2;
|
|
|
|
for (j=0 ; j<3 ; j++)
|
|
if (pflags & (DF_ANGLES << j))
|
|
protocollen += 2;
|
|
|
|
|
|
if (pflags & DF_MODEL)
|
|
protocollen += 1;
|
|
|
|
if (pflags & DF_SKINNUM)
|
|
protocollen += 1;
|
|
|
|
if (pflags & DF_EFFECTS)
|
|
protocollen += 1;
|
|
|
|
if (pflags & DF_WEAPONFRAME)
|
|
protocollen += 1;
|
|
|
|
/*
|
|
if (pflags & PF_MSEC)
|
|
protocollen+=1;
|
|
|
|
if (pflags & PF_COMMAND)
|
|
{
|
|
Con_Printf("svc_playerinfo PF_COMMAND not expected in mvd\n");
|
|
protocollen+=500;
|
|
}
|
|
|
|
for (i=0 ; i<3 ; i++)
|
|
if (pflags & (PF_VELOCITY1<<i) )
|
|
protocollen+=2;
|
|
|
|
if (pflags & PF_MODEL)
|
|
protocollen+=1;
|
|
|
|
if (pflags & PF_SKINNUM)
|
|
protocollen+=1;
|
|
|
|
if (pflags & PF_EFFECTS)
|
|
protocollen+=1;
|
|
|
|
if (pflags & PF_WEAPONFRAME)
|
|
protocollen+=1;
|
|
*/
|
|
}
|
|
break;
|
|
|
|
case svc_packetentities:
|
|
case svc_deltapacketentities:
|
|
if (!data) //two last bytes are 0.
|
|
{
|
|
unsigned short s;
|
|
i = majortype-svc_packetentities+1;
|
|
buffer[bufferlen] = data;
|
|
bufferlen++;
|
|
while (1)
|
|
{
|
|
s = buffer[i] + buffer[i+1]*256;
|
|
i+=2;
|
|
if (i > bufferlen)
|
|
break;
|
|
if (!s)
|
|
{
|
|
if (i <= bufferlen)
|
|
protocollen = bufferlen;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
s &= ~511;
|
|
if (s & U_MOREBITS)
|
|
{
|
|
s |= buffer[i];
|
|
i++;
|
|
}
|
|
|
|
if (s & U_MODEL)
|
|
i++;
|
|
|
|
if (s & U_FRAME)
|
|
i++;
|
|
|
|
if (s & U_COLORMAP)
|
|
i++;
|
|
|
|
if (s & U_SKIN)
|
|
i++;
|
|
|
|
if (s & U_EFFECTS)
|
|
i++;
|
|
|
|
if (s & U_ORIGIN1)
|
|
i+=2;
|
|
|
|
if (s & U_ANGLE1)
|
|
i++;
|
|
|
|
if (s & U_ORIGIN2)
|
|
i+=2;
|
|
|
|
if (s & U_ANGLE2)
|
|
i++;
|
|
|
|
if (s & U_ORIGIN3)
|
|
i+=2;
|
|
|
|
if (s & U_ANGLE3)
|
|
i++;
|
|
}
|
|
}
|
|
bufferlen--;
|
|
}
|
|
break;
|
|
|
|
case svc_lightstyle:
|
|
if (!data && bufferlen>=2)
|
|
protocollen = bufferlen+1;
|
|
break;
|
|
|
|
case svc_serverinfo: //looking for two null terminators
|
|
if (!data) //gotta be the second.
|
|
{
|
|
for (i = 0; i < bufferlen; i++)
|
|
{
|
|
if (!buffer[i]) //we already wrote one.
|
|
{
|
|
protocollen = bufferlen+1; //so this is the last qbyte
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case svc_sound:
|
|
if (bufferlen == 3) //decide after 3
|
|
{
|
|
unsigned short s;
|
|
i = 10;
|
|
s = buffer[1] + buffer[2]*256;
|
|
if (s & SND_VOLUME)
|
|
i++;
|
|
if (s & SND_ATTENUATION)
|
|
i++;
|
|
protocollen = i;
|
|
}
|
|
break;
|
|
|
|
case svc_setinfo:
|
|
if (!data && bufferlen>2)
|
|
{
|
|
for (i = 2; i < bufferlen; i++)
|
|
{
|
|
if (!buffer[i])
|
|
{
|
|
protocollen = bufferlen+1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case svc_centerprint:
|
|
if (!data && bufferlen>=1)
|
|
protocollen = bufferlen+1;
|
|
break;
|
|
case svc_print:
|
|
if (!data && bufferlen>=2)
|
|
protocollen = bufferlen+1;
|
|
break;
|
|
|
|
case svc_nails:
|
|
if (bufferlen == 1)
|
|
{
|
|
i = data; //first qbyte is id, second, (the current, about to be written) is number of nails.
|
|
protocollen = (i * 6) + 2;
|
|
}
|
|
break;
|
|
case svc_nails2:
|
|
if (bufferlen == 1)
|
|
{
|
|
i = data; //first qbyte is id, second, (the current, about to be written) is number of nails.
|
|
protocollen = (i * 7) + 2;
|
|
}
|
|
break;
|
|
default:
|
|
Con_Printf("bad protocol %i\n", (int)data);
|
|
protocollen = sizeof(buffer);
|
|
net_message.cursize=0;
|
|
data = svc_nop;
|
|
protocollen = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
NPP_AddData(&data, sizeof(qbyte));
|
|
NPP_MVDCheckFlush();
|
|
}
|
|
|