#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 (fixme: ignored) #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; coorddata cd; memcpy(&cd, &buffer[multicastpos+sizeofcoord*0], sizeofcoord); org[0] = MSG_FromCoord(cd, sizeofcoord); memcpy(&cd, &buffer[multicastpos+sizeofcoord*1], sizeofcoord); org[1] = MSG_FromCoord(cd, sizeofcoord); memcpy(&cd, &buffer[multicastpos+sizeofcoord*2], sizeofcoord); org[2] = MSG_FromCoord(cd, sizeofcoord); 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("nq: 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 = sizeofcoord*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 = sizeofcoord*3+sizeof(qbyte)*3; break; case TE_EXPLOSION: case TE_SPIKE: case TE_SUPERSPIKE: multicastpos=2; multicasttype=MULTICAST_PHS_R; protocollen = sizeofcoord*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 = sizeofcoord*3+sizeof(qbyte)*2; break; case TE_EXPLOSION3_NEH: protocollen = sizeof(qbyte) + sizeofcoord*6; ignoreprotocol = true; break; case NQTE_EXPLOSION2: protocollen = sizeof(qbyte)*4 + sizeofcoord*3; multicastpos=2; multicasttype=MULTICAST_PHS_R; break; case TE_EXPLOSIONSMALL2: data = TE_EXPLOSION; protocollen = sizeof(qbyte)*2 + sizeofcoord*3; multicastpos=2; multicasttype=MULTICAST_PHS; break; case TE_RAILTRAIL: protocollen = sizeofcoord*6+sizeof(qbyte)*1; multicastpos=2; 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 = sizeofcoord*6+sizeof(short)+sizeof(qbyte)*(2+2); multicastpos = 8; multicasttype=MULTICAST_PHS; break; case TE_STREAM_COLORBEAM: protocollen = sizeofcoord*6+sizeof(short)+sizeof(qbyte)*(3+2); multicastpos = 8; multicasttype=MULTICAST_PHS; break; case 74: //TE_FLAMEJET protocollen = sizeofcoord*6 +sizeof(qbyte)*3; multicastpos = 2; multicasttype=MULTICAST_PVS; break; case 76: protocollen = sizeofcoord*9+sizeof(qbyte)*2; multicastpos = 2; multicasttype=MULTICAST_PHS; break; case 79: protocollen = sizeofcoord*6+sizeof(qbyte)*3; multicastpos = 2; 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); if (!protocollen) //these protocols take strings, and are thus dynamically sized. { switch(majortype) { case svc_setname: case svc_stufftext: case svc_centerprint: protocollen = bufferlen; break; } } 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; coorddata cd; memcpy(&cd, &buffer[multicastpos+sizeofcoord*0], sizeofcoord); org[0] = MSG_FromCoord(cd, sizeofcoord); memcpy(&cd, &buffer[multicastpos+sizeofcoord*1], sizeofcoord); org[1] = MSG_FromCoord(cd, sizeofcoord); memcpy(&cd, &buffer[multicastpos+sizeofcoord*2], sizeofcoord); org[2] = MSG_FromCoord(cd, sizeofcoord); 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 = sizeofcoord*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 = sizeofcoord*3+sizeof(qbyte)*3; break; case TE_LIGHTNINGBLOOD: case TE_EXPLOSION: case TE_SPIKE: case TE_SUPERSPIKE: multicastpos=2; multicasttype=MULTICAST_PHS_R; protocollen = sizeofcoord*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 = sizeofcoord*3+sizeof(qbyte)*2; break; case TE_RAILTRAIL: multicastpos=1; multicasttype=MULTICAST_PVS; protocollen = sizeofcoord*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 datas = (int)(in*8); float dataf = in; 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(float)); ClientReliableWrite_Coord(cl, in); } } else MSG_WriteCoord (WriteDest(dest), in); #endif if (sizeofcoord==4) { dataf = LittleFloat(dataf); NPP_AddData(&dataf, sizeof(float)); } else { datas = LittleShort(datas); NPP_AddData(&datas, 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; coorddata cd; memcpy(&cd, &buffer[multicastpos+sizeofcoord*0], sizeofcoord); org[0] = MSG_FromCoord(cd, sizeofcoord); memcpy(&cd, &buffer[multicastpos+sizeofcoord*1], sizeofcoord); org[1] = MSG_FromCoord(cd, sizeofcoord); memcpy(&cd, &buffer[multicastpos+sizeofcoord*2], sizeofcoord); org[2] = MSG_FromCoord(cd, sizeofcoord); 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("mvd: 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< 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("mvd: 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(); }