diff --git a/code/client/cl_input.c b/code/client/cl_input.c index a9725363..ff0e5b16 100644 --- a/code/client/cl_input.c +++ b/code/client/cl_input.c @@ -760,6 +760,8 @@ void CL_WritePacket( void ) { #if USE_VOIP if (clc.voipOutgoingDataSize > 0) { // only send if data. + MSG_WriteByte (&buf, clc_EOF); // placate legacy servers. + MSG_WriteByte (&buf, clc_extension); MSG_WriteByte (&buf, clc_voip); MSG_WriteByte (&buf, clc.voipOutgoingGeneration); MSG_WriteLong (&buf, clc.voipOutgoingSequence); diff --git a/code/client/cl_parse.c b/code/client/cl_parse.c index d80d7d29..294e46e3 100644 --- a/code/client/cl_parse.c +++ b/code/client/cl_parse.c @@ -34,10 +34,8 @@ char *svc_strings[256] = { "svc_download", "svc_snapshot", "svc_EOF", - -#if USE_VOIP - "svc_voip" -#endif + "svc_extension", + "svc_voip", }; void SHOWNET( msg_t *msg, char *s) { @@ -854,13 +852,26 @@ void CL_ParseServerMessage( msg_t *msg ) { cmd = MSG_ReadByte( msg ); - if ( cmd == svc_EOF) { + // See if this is an extension command after the EOF, which means we + // got data that a legacy client should ignore. + if ((cmd == svc_EOF) && (MSG_LookaheadByte( msg ) == svc_extension)) { + SHOWNET( msg, "EXTENSION" ); + MSG_ReadByte( msg ); // throw the svc_extension byte away. + cmd = MSG_ReadByte( msg ); // something legacy clients can't do! + // sometimes you get a svc_extension at end of stream...dangling + // bits in the huffman decoder giving a bogus value? + if (cmd == -1) { + cmd = svc_EOF; + } + } + + if (cmd == svc_EOF) { SHOWNET( msg, "END OF MESSAGE" ); break; } if ( cl_shownet->integer >= 2 ) { - if ( !svc_strings[cmd] ) { + if ( (cmd < 0) || (!svc_strings[cmd]) ) { Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd ); } else { SHOWNET( msg, svc_strings[cmd] ); @@ -886,11 +897,11 @@ void CL_ParseServerMessage( msg_t *msg ) { case svc_download: CL_ParseDownload( msg ); break; -#if USE_VOIP case svc_voip: +#if USE_VOIP CL_ParseVoip( msg ); - break; #endif + break; } } } diff --git a/code/qcommon/huffman.c b/code/qcommon/huffman.c index b230b5be..2ff4ae50 100644 --- a/code/qcommon/huffman.c +++ b/code/qcommon/huffman.c @@ -39,6 +39,16 @@ void Huff_putBit( int bit, byte *fout, int *offset) { *offset = bloc; } +int Huff_getBloc(void) +{ + return bloc; +} + +void Huff_setBloc(int _bloc) +{ + bloc = _bloc; +} + int Huff_getBit( byte *fin, int *offset) { int t; bloc = *offset; diff --git a/code/qcommon/msg.c b/code/qcommon/msg.c index b26cf8fe..3840a8c2 100644 --- a/code/qcommon/msg.c +++ b/code/qcommon/msg.c @@ -389,6 +389,17 @@ int MSG_ReadByte( msg_t *msg ) { return c; } +int MSG_LookaheadByte( msg_t *msg ) { + const int bloc = Huff_getBloc(); + const int readcount = msg->readcount; + const int bit = msg->bit; + int c = MSG_ReadByte(msg); + Huff_setBloc(bloc); + msg->readcount = readcount; + msg->bit = bit; + return c; +} + int MSG_ReadShort( msg_t *msg ) { int c; diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h index f535d682..58b0a294 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -92,7 +92,7 @@ char *MSG_ReadBigString (msg_t *sb); char *MSG_ReadStringLine (msg_t *sb); float MSG_ReadAngle16 (msg_t *sb); void MSG_ReadData (msg_t *sb, void *buffer, int size); - +int MSG_LookaheadByte (msg_t *msg); void MSG_WriteDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to ); void MSG_ReadDeltaUsercmd( msg_t *msg, struct usercmd_s *from, struct usercmd_s *to ); @@ -276,9 +276,10 @@ enum svc_ops_e { svc_snapshot, svc_EOF, -#if USE_VOIP - svc_voip -#endif + // svc_extension follows a svc_EOF, followed by another svc_* ... + // this keeps legacy clients compatible. + svc_extension, + svc_voip, // not wrapped in USE_VOIP, so this value is reserved. }; @@ -293,9 +294,10 @@ enum clc_ops_e { clc_clientCommand, // [string] message clc_EOF, -#if USE_VOIP - clc_voip, // packet of voice data. -#endif + // clc_extension follows a clc_EOF, followed by another clc_* ... + // this keeps legacy servers compatible. + clc_extension, + clc_voip, // not wrapped in USE_VOIP, so this value is reserved. }; /* @@ -1112,6 +1114,11 @@ void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset); void Huff_putBit( int bit, byte *fout, int *offset); int Huff_getBit( byte *fout, int *offset); +// don't use if you don't know what you're doing. +int Huff_getBloc(void); +void Huff_setBloc(int _bloc); + + extern huffman_t clientHuffTables; #define SV_ENCODE_START 4 diff --git a/code/server/sv_client.c b/code/server/sv_client.c index 2528e64a..180bbf38 100644 --- a/code/server/sv_client.c +++ b/code/server/sv_client.c @@ -1108,6 +1108,14 @@ void SV_WriteVoipToClient( client_t *cl, msg_t *msg ) if (totalbytes > MAX_DOWNLOAD_BLKSIZE) break; + // You have to start with a svc_EOF, so legacy clients drop the + // rest of this packet. Otherwise, those without VoIP support will + // see the svc_voip command, then panic and disconnect. + // Generally we don't send VoIP packets to legacy clients, but this + // serves as both a safety measure and a means to keep demo files + // compatible. + MSG_WriteByte( msg, svc_EOF ); + MSG_WriteByte( msg, svc_extension ); MSG_WriteByte( msg, svc_voip ); MSG_WriteShort( msg, packet->sender ); MSG_WriteByte( msg, (byte) packet->generation ); @@ -1880,9 +1888,23 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { // read optional clientCommand strings do { c = MSG_ReadByte( msg ); + + // See if this is an extension command after the EOF, which means we + // got data that a legacy server should ignore. + if ((c == clc_EOF) && (MSG_LookaheadByte( msg ) == clc_extension)) { + MSG_ReadByte( msg ); // throw the clc_extension byte away. + c = MSG_ReadByte( msg ); // something legacy servers can't do! + // sometimes you get a clc_extension at end of stream...dangling + // bits in the huffman decoder giving a bogus value? + if (c == -1) { + c = clc_EOF; + } + } + if ( c == clc_EOF ) { break; } + if ( c != clc_clientCommand ) { break; } @@ -1899,8 +1921,8 @@ void SV_ExecuteClientMessage( client_t *cl, msg_t *msg ) { SV_UserMove( cl, msg, qtrue ); } else if ( c == clc_moveNoDelta ) { SV_UserMove( cl, msg, qfalse ); -#if USE_VOIP } else if ( c == clc_voip ) { +#if USE_VOIP SV_UserVoip( cl, msg ); #endif } else if ( c != clc_EOF ) {