Quake-III-Arena

Quake III Arena GPL Source Release
Log | Files | Refs

cl_parse.c (17021B)


      1 /*
      2 ===========================================================================
      3 Copyright (C) 1999-2005 Id Software, Inc.
      4 
      5 This file is part of Quake III Arena source code.
      6 
      7 Quake III Arena source code is free software; you can redistribute it
      8 and/or modify it under the terms of the GNU General Public License as
      9 published by the Free Software Foundation; either version 2 of the License,
     10 or (at your option) any later version.
     11 
     12 Quake III Arena source code is distributed in the hope that it will be
     13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
     14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15 GNU General Public License for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with Foobar; if not, write to the Free Software
     19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     20 ===========================================================================
     21 */
     22 // cl_parse.c  -- parse a message received from the server
     23 
     24 #include "client.h"
     25 
     26 char *svc_strings[256] = {
     27 	"svc_bad",
     28 
     29 	"svc_nop",
     30 	"svc_gamestate",
     31 	"svc_configstring",
     32 	"svc_baseline",	
     33 	"svc_serverCommand",
     34 	"svc_download",
     35 	"svc_snapshot"
     36 };
     37 
     38 void SHOWNET( msg_t *msg, char *s) {
     39 	if ( cl_shownet->integer >= 2) {
     40 		Com_Printf ("%3i:%s\n", msg->readcount-1, s);
     41 	}
     42 }
     43 
     44 
     45 /*
     46 =========================================================================
     47 
     48 MESSAGE PARSING
     49 
     50 =========================================================================
     51 */
     52 
     53 /*
     54 ==================
     55 CL_DeltaEntity
     56 
     57 Parses deltas from the given base and adds the resulting entity
     58 to the current frame
     59 ==================
     60 */
     61 void CL_DeltaEntity (msg_t *msg, clSnapshot_t *frame, int newnum, entityState_t *old, 
     62 					 qboolean unchanged) {
     63 	entityState_t	*state;
     64 
     65 	// save the parsed entity state into the big circular buffer so
     66 	// it can be used as the source for a later delta
     67 	state = &cl.parseEntities[cl.parseEntitiesNum & (MAX_PARSE_ENTITIES-1)];
     68 
     69 	if ( unchanged ) {
     70 		*state = *old;
     71 	} else {
     72 		MSG_ReadDeltaEntity( msg, old, state, newnum );
     73 	}
     74 
     75 	if ( state->number == (MAX_GENTITIES-1) ) {
     76 		return;		// entity was delta removed
     77 	}
     78 	cl.parseEntitiesNum++;
     79 	frame->numEntities++;
     80 }
     81 
     82 /*
     83 ==================
     84 CL_ParsePacketEntities
     85 
     86 ==================
     87 */
     88 void CL_ParsePacketEntities( msg_t *msg, clSnapshot_t *oldframe, clSnapshot_t *newframe) {
     89 	int			newnum;
     90 	entityState_t	*oldstate;
     91 	int			oldindex, oldnum;
     92 
     93 	newframe->parseEntitiesNum = cl.parseEntitiesNum;
     94 	newframe->numEntities = 0;
     95 
     96 	// delta from the entities present in oldframe
     97 	oldindex = 0;
     98 	oldstate = NULL;
     99 	if (!oldframe) {
    100 		oldnum = 99999;
    101 	} else {
    102 		if ( oldindex >= oldframe->numEntities ) {
    103 			oldnum = 99999;
    104 		} else {
    105 			oldstate = &cl.parseEntities[
    106 				(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
    107 			oldnum = oldstate->number;
    108 		}
    109 	}
    110 
    111 	while ( 1 ) {
    112 		// read the entity index number
    113 		newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
    114 
    115 		if ( newnum == (MAX_GENTITIES-1) ) {
    116 			break;
    117 		}
    118 
    119 		if ( msg->readcount > msg->cursize ) {
    120 			Com_Error (ERR_DROP,"CL_ParsePacketEntities: end of message");
    121 		}
    122 
    123 		while ( oldnum < newnum ) {
    124 			// one or more entities from the old packet are unchanged
    125 			if ( cl_shownet->integer == 3 ) {
    126 				Com_Printf ("%3i:  unchanged: %i\n", msg->readcount, oldnum);
    127 			}
    128 			CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
    129 			
    130 			oldindex++;
    131 
    132 			if ( oldindex >= oldframe->numEntities ) {
    133 				oldnum = 99999;
    134 			} else {
    135 				oldstate = &cl.parseEntities[
    136 					(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
    137 				oldnum = oldstate->number;
    138 			}
    139 		}
    140 		if (oldnum == newnum) {
    141 			// delta from previous state
    142 			if ( cl_shownet->integer == 3 ) {
    143 				Com_Printf ("%3i:  delta: %i\n", msg->readcount, newnum);
    144 			}
    145 			CL_DeltaEntity( msg, newframe, newnum, oldstate, qfalse );
    146 
    147 			oldindex++;
    148 
    149 			if ( oldindex >= oldframe->numEntities ) {
    150 				oldnum = 99999;
    151 			} else {
    152 				oldstate = &cl.parseEntities[
    153 					(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
    154 				oldnum = oldstate->number;
    155 			}
    156 			continue;
    157 		}
    158 
    159 		if ( oldnum > newnum ) {
    160 			// delta from baseline
    161 			if ( cl_shownet->integer == 3 ) {
    162 				Com_Printf ("%3i:  baseline: %i\n", msg->readcount, newnum);
    163 			}
    164 			CL_DeltaEntity( msg, newframe, newnum, &cl.entityBaselines[newnum], qfalse );
    165 			continue;
    166 		}
    167 
    168 	}
    169 
    170 	// any remaining entities in the old frame are copied over
    171 	while ( oldnum != 99999 ) {
    172 		// one or more entities from the old packet are unchanged
    173 		if ( cl_shownet->integer == 3 ) {
    174 			Com_Printf ("%3i:  unchanged: %i\n", msg->readcount, oldnum);
    175 		}
    176 		CL_DeltaEntity( msg, newframe, oldnum, oldstate, qtrue );
    177 		
    178 		oldindex++;
    179 
    180 		if ( oldindex >= oldframe->numEntities ) {
    181 			oldnum = 99999;
    182 		} else {
    183 			oldstate = &cl.parseEntities[
    184 				(oldframe->parseEntitiesNum + oldindex) & (MAX_PARSE_ENTITIES-1)];
    185 			oldnum = oldstate->number;
    186 		}
    187 	}
    188 }
    189 
    190 
    191 /*
    192 ================
    193 CL_ParseSnapshot
    194 
    195 If the snapshot is parsed properly, it will be copied to
    196 cl.snap and saved in cl.snapshots[].  If the snapshot is invalid
    197 for any reason, no changes to the state will be made at all.
    198 ================
    199 */
    200 void CL_ParseSnapshot( msg_t *msg ) {
    201 	int			len;
    202 	clSnapshot_t	*old;
    203 	clSnapshot_t	newSnap;
    204 	int			deltaNum;
    205 	int			oldMessageNum;
    206 	int			i, packetNum;
    207 
    208 	// get the reliable sequence acknowledge number
    209 	// NOTE: now sent with all server to client messages
    210 	//clc.reliableAcknowledge = MSG_ReadLong( msg );
    211 
    212 	// read in the new snapshot to a temporary buffer
    213 	// we will only copy to cl.snap if it is valid
    214 	Com_Memset (&newSnap, 0, sizeof(newSnap));
    215 
    216 	// we will have read any new server commands in this
    217 	// message before we got to svc_snapshot
    218 	newSnap.serverCommandNum = clc.serverCommandSequence;
    219 
    220 	newSnap.serverTime = MSG_ReadLong( msg );
    221 
    222 	newSnap.messageNum = clc.serverMessageSequence;
    223 
    224 	deltaNum = MSG_ReadByte( msg );
    225 	if ( !deltaNum ) {
    226 		newSnap.deltaNum = -1;
    227 	} else {
    228 		newSnap.deltaNum = newSnap.messageNum - deltaNum;
    229 	}
    230 	newSnap.snapFlags = MSG_ReadByte( msg );
    231 
    232 	// If the frame is delta compressed from data that we
    233 	// no longer have available, we must suck up the rest of
    234 	// the frame, but not use it, then ask for a non-compressed
    235 	// message 
    236 	if ( newSnap.deltaNum <= 0 ) {
    237 		newSnap.valid = qtrue;		// uncompressed frame
    238 		old = NULL;
    239 		clc.demowaiting = qfalse;	// we can start recording now
    240 	} else {
    241 		old = &cl.snapshots[newSnap.deltaNum & PACKET_MASK];
    242 		if ( !old->valid ) {
    243 			// should never happen
    244 			Com_Printf ("Delta from invalid frame (not supposed to happen!).\n");
    245 		} else if ( old->messageNum != newSnap.deltaNum ) {
    246 			// The frame that the server did the delta from
    247 			// is too old, so we can't reconstruct it properly.
    248 			Com_Printf ("Delta frame too old.\n");
    249 		} else if ( cl.parseEntitiesNum - old->parseEntitiesNum > MAX_PARSE_ENTITIES-128 ) {
    250 			Com_Printf ("Delta parseEntitiesNum too old.\n");
    251 		} else {
    252 			newSnap.valid = qtrue;	// valid delta parse
    253 		}
    254 	}
    255 
    256 	// read areamask
    257 	len = MSG_ReadByte( msg );
    258 	MSG_ReadData( msg, &newSnap.areamask, len);
    259 
    260 	// read playerinfo
    261 	SHOWNET( msg, "playerstate" );
    262 	if ( old ) {
    263 		MSG_ReadDeltaPlayerstate( msg, &old->ps, &newSnap.ps );
    264 	} else {
    265 		MSG_ReadDeltaPlayerstate( msg, NULL, &newSnap.ps );
    266 	}
    267 
    268 	// read packet entities
    269 	SHOWNET( msg, "packet entities" );
    270 	CL_ParsePacketEntities( msg, old, &newSnap );
    271 
    272 	// if not valid, dump the entire thing now that it has
    273 	// been properly read
    274 	if ( !newSnap.valid ) {
    275 		return;
    276 	}
    277 
    278 	// clear the valid flags of any snapshots between the last
    279 	// received and this one, so if there was a dropped packet
    280 	// it won't look like something valid to delta from next
    281 	// time we wrap around in the buffer
    282 	oldMessageNum = cl.snap.messageNum + 1;
    283 
    284 	if ( newSnap.messageNum - oldMessageNum >= PACKET_BACKUP ) {
    285 		oldMessageNum = newSnap.messageNum - ( PACKET_BACKUP - 1 );
    286 	}
    287 	for ( ; oldMessageNum < newSnap.messageNum ; oldMessageNum++ ) {
    288 		cl.snapshots[oldMessageNum & PACKET_MASK].valid = qfalse;
    289 	}
    290 
    291 	// copy to the current good spot
    292 	cl.snap = newSnap;
    293 	cl.snap.ping = 999;
    294 	// calculate ping time
    295 	for ( i = 0 ; i < PACKET_BACKUP ; i++ ) {
    296 		packetNum = ( clc.netchan.outgoingSequence - 1 - i ) & PACKET_MASK;
    297 		if ( cl.snap.ps.commandTime >= cl.outPackets[ packetNum ].p_serverTime ) {
    298 			cl.snap.ping = cls.realtime - cl.outPackets[ packetNum ].p_realtime;
    299 			break;
    300 		}
    301 	}
    302 	// save the frame off in the backup array for later delta comparisons
    303 	cl.snapshots[cl.snap.messageNum & PACKET_MASK] = cl.snap;
    304 
    305 	if (cl_shownet->integer == 3) {
    306 		Com_Printf( "   snapshot:%i  delta:%i  ping:%i\n", cl.snap.messageNum,
    307 		cl.snap.deltaNum, cl.snap.ping );
    308 	}
    309 
    310 	cl.newSnapshots = qtrue;
    311 }
    312 
    313 
    314 //=====================================================================
    315 
    316 int cl_connectedToPureServer;
    317 
    318 /*
    319 ==================
    320 CL_SystemInfoChanged
    321 
    322 The systeminfo configstring has been changed, so parse
    323 new information out of it.  This will happen at every
    324 gamestate, and possibly during gameplay.
    325 ==================
    326 */
    327 void CL_SystemInfoChanged( void ) {
    328 	char			*systemInfo;
    329 	const char		*s, *t;
    330 	char			key[BIG_INFO_KEY];
    331 	char			value[BIG_INFO_VALUE];
    332 	qboolean		gameSet;
    333 
    334 	systemInfo = cl.gameState.stringData + cl.gameState.stringOffsets[ CS_SYSTEMINFO ];
    335 	// NOTE TTimo:
    336 	// when the serverId changes, any further messages we send to the server will use this new serverId
    337 	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=475
    338 	// in some cases, outdated cp commands might get sent with this news serverId
    339 	cl.serverId = atoi( Info_ValueForKey( systemInfo, "sv_serverid" ) );
    340 
    341 	// don't set any vars when playing a demo
    342 	if ( clc.demoplaying ) {
    343 		return;
    344 	}
    345 
    346 	s = Info_ValueForKey( systemInfo, "sv_cheats" );
    347 	if ( atoi(s) == 0 ) {
    348 		Cvar_SetCheatState();
    349 	}
    350 
    351 	// check pure server string
    352 	s = Info_ValueForKey( systemInfo, "sv_paks" );
    353 	t = Info_ValueForKey( systemInfo, "sv_pakNames" );
    354 	FS_PureServerSetLoadedPaks( s, t );
    355 
    356 	s = Info_ValueForKey( systemInfo, "sv_referencedPaks" );
    357 	t = Info_ValueForKey( systemInfo, "sv_referencedPakNames" );
    358 	FS_PureServerSetReferencedPaks( s, t );
    359 
    360 	gameSet = qfalse;
    361 	// scan through all the variables in the systeminfo and locally set cvars to match
    362 	s = systemInfo;
    363 	while ( s ) {
    364 		Info_NextPair( &s, key, value );
    365 		if ( !key[0] ) {
    366 			break;
    367 		}
    368 		// ehw!
    369 		if ( !Q_stricmp( key, "fs_game" ) ) {
    370 			gameSet = qtrue;
    371 		}
    372 
    373 		Cvar_Set( key, value );
    374 	}
    375 	// if game folder should not be set and it is set at the client side
    376 	if ( !gameSet && *Cvar_VariableString("fs_game") ) {
    377 		Cvar_Set( "fs_game", "" );
    378 	}
    379 	cl_connectedToPureServer = Cvar_VariableValue( "sv_pure" );
    380 }
    381 
    382 /*
    383 ==================
    384 CL_ParseGamestate
    385 ==================
    386 */
    387 void CL_ParseGamestate( msg_t *msg ) {
    388 	int				i;
    389 	entityState_t	*es;
    390 	int				newnum;
    391 	entityState_t	nullstate;
    392 	int				cmd;
    393 	char			*s;
    394 
    395 	Con_Close();
    396 
    397 	clc.connectPacketCount = 0;
    398 
    399 	// wipe local client state
    400 	CL_ClearState();
    401 
    402 	// a gamestate always marks a server command sequence
    403 	clc.serverCommandSequence = MSG_ReadLong( msg );
    404 
    405 	// parse all the configstrings and baselines
    406 	cl.gameState.dataCount = 1;	// leave a 0 at the beginning for uninitialized configstrings
    407 	while ( 1 ) {
    408 		cmd = MSG_ReadByte( msg );
    409 
    410 		if ( cmd == svc_EOF ) {
    411 			break;
    412 		}
    413 		
    414 		if ( cmd == svc_configstring ) {
    415 			int		len;
    416 
    417 			i = MSG_ReadShort( msg );
    418 			if ( i < 0 || i >= MAX_CONFIGSTRINGS ) {
    419 				Com_Error( ERR_DROP, "configstring > MAX_CONFIGSTRINGS" );
    420 			}
    421 			s = MSG_ReadBigString( msg );
    422 			len = strlen( s );
    423 
    424 			if ( len + 1 + cl.gameState.dataCount > MAX_GAMESTATE_CHARS ) {
    425 				Com_Error( ERR_DROP, "MAX_GAMESTATE_CHARS exceeded" );
    426 			}
    427 
    428 			// append it to the gameState string buffer
    429 			cl.gameState.stringOffsets[ i ] = cl.gameState.dataCount;
    430 			Com_Memcpy( cl.gameState.stringData + cl.gameState.dataCount, s, len + 1 );
    431 			cl.gameState.dataCount += len + 1;
    432 		} else if ( cmd == svc_baseline ) {
    433 			newnum = MSG_ReadBits( msg, GENTITYNUM_BITS );
    434 			if ( newnum < 0 || newnum >= MAX_GENTITIES ) {
    435 				Com_Error( ERR_DROP, "Baseline number out of range: %i", newnum );
    436 			}
    437 			Com_Memset (&nullstate, 0, sizeof(nullstate));
    438 			es = &cl.entityBaselines[ newnum ];
    439 			MSG_ReadDeltaEntity( msg, &nullstate, es, newnum );
    440 		} else {
    441 			Com_Error( ERR_DROP, "CL_ParseGamestate: bad command byte" );
    442 		}
    443 	}
    444 
    445 	clc.clientNum = MSG_ReadLong(msg);
    446 	// read the checksum feed
    447 	clc.checksumFeed = MSG_ReadLong( msg );
    448 
    449 	// parse serverId and other cvars
    450 	CL_SystemInfoChanged();
    451 
    452 	// reinitialize the filesystem if the game directory has changed
    453   FS_ConditionalRestart( clc.checksumFeed );
    454 
    455 	// This used to call CL_StartHunkUsers, but now we enter the download state before loading the
    456 	// cgame
    457 	CL_InitDownloads();
    458 
    459 	// make sure the game starts
    460 	Cvar_Set( "cl_paused", "0" );
    461 }
    462 
    463 
    464 //=====================================================================
    465 
    466 /*
    467 =====================
    468 CL_ParseDownload
    469 
    470 A download message has been received from the server
    471 =====================
    472 */
    473 void CL_ParseDownload ( msg_t *msg ) {
    474 	int		size;
    475 	unsigned char data[MAX_MSGLEN];
    476 	int block;
    477 
    478 	// read the data
    479 	block = MSG_ReadShort ( msg );
    480 
    481 	if ( !block )
    482 	{
    483 		// block zero is special, contains file size
    484 		clc.downloadSize = MSG_ReadLong ( msg );
    485 
    486 		Cvar_SetValue( "cl_downloadSize", clc.downloadSize );
    487 
    488 		if (clc.downloadSize < 0)
    489 		{
    490 			Com_Error(ERR_DROP, MSG_ReadString( msg ) );
    491 			return;
    492 		}
    493 	}
    494 
    495 	size = MSG_ReadShort ( msg );
    496 	if (size > 0)
    497 		MSG_ReadData( msg, data, size );
    498 
    499 	if (clc.downloadBlock != block) {
    500 		Com_DPrintf( "CL_ParseDownload: Expected block %d, got %d\n", clc.downloadBlock, block);
    501 		return;
    502 	}
    503 
    504 	// open the file if not opened yet
    505 	if (!clc.download)
    506 	{
    507 		if (!*clc.downloadTempName) {
    508 			Com_Printf("Server sending download, but no download was requested\n");
    509 			CL_AddReliableCommand( "stopdl" );
    510 			return;
    511 		}
    512 
    513 		clc.download = FS_SV_FOpenFileWrite( clc.downloadTempName );
    514 
    515 		if (!clc.download) {
    516 			Com_Printf( "Could not create %s\n", clc.downloadTempName );
    517 			CL_AddReliableCommand( "stopdl" );
    518 			CL_NextDownload();
    519 			return;
    520 		}
    521 	}
    522 
    523 	if (size)
    524 		FS_Write( data, size, clc.download );
    525 
    526 	CL_AddReliableCommand( va("nextdl %d", clc.downloadBlock) );
    527 	clc.downloadBlock++;
    528 
    529 	clc.downloadCount += size;
    530 
    531 	// So UI gets access to it
    532 	Cvar_SetValue( "cl_downloadCount", clc.downloadCount );
    533 
    534 	if (!size) { // A zero length block means EOF
    535 		if (clc.download) {
    536 			FS_FCloseFile( clc.download );
    537 			clc.download = 0;
    538 
    539 			// rename the file
    540 			FS_SV_Rename ( clc.downloadTempName, clc.downloadName );
    541 		}
    542 		*clc.downloadTempName = *clc.downloadName = 0;
    543 		Cvar_Set( "cl_downloadName", "" );
    544 
    545 		// send intentions now
    546 		// We need this because without it, we would hold the last nextdl and then start
    547 		// loading right away.  If we take a while to load, the server is happily trying
    548 		// to send us that last block over and over.
    549 		// Write it twice to help make sure we acknowledge the download
    550 		CL_WritePacket();
    551 		CL_WritePacket();
    552 
    553 		// get another file if needed
    554 		CL_NextDownload ();
    555 	}
    556 }
    557 
    558 /*
    559 =====================
    560 CL_ParseCommandString
    561 
    562 Command strings are just saved off until cgame asks for them
    563 when it transitions a snapshot
    564 =====================
    565 */
    566 void CL_ParseCommandString( msg_t *msg ) {
    567 	char	*s;
    568 	int		seq;
    569 	int		index;
    570 
    571 	seq = MSG_ReadLong( msg );
    572 	s = MSG_ReadString( msg );
    573 
    574 	// see if we have already executed stored it off
    575 	if ( clc.serverCommandSequence >= seq ) {
    576 		return;
    577 	}
    578 	clc.serverCommandSequence = seq;
    579 
    580 	index = seq & (MAX_RELIABLE_COMMANDS-1);
    581 	Q_strncpyz( clc.serverCommands[ index ], s, sizeof( clc.serverCommands[ index ] ) );
    582 }
    583 
    584 
    585 /*
    586 =====================
    587 CL_ParseServerMessage
    588 =====================
    589 */
    590 void CL_ParseServerMessage( msg_t *msg ) {
    591 	int			cmd;
    592 
    593 	if ( cl_shownet->integer == 1 ) {
    594 		Com_Printf ("%i ",msg->cursize);
    595 	} else if ( cl_shownet->integer >= 2 ) {
    596 		Com_Printf ("------------------\n");
    597 	}
    598 
    599 	MSG_Bitstream(msg);
    600 
    601 	// get the reliable sequence acknowledge number
    602 	clc.reliableAcknowledge = MSG_ReadLong( msg );
    603 	// 
    604 	if ( clc.reliableAcknowledge < clc.reliableSequence - MAX_RELIABLE_COMMANDS ) {
    605 		clc.reliableAcknowledge = clc.reliableSequence;
    606 	}
    607 
    608 	//
    609 	// parse the message
    610 	//
    611 	while ( 1 ) {
    612 		if ( msg->readcount > msg->cursize ) {
    613 			Com_Error (ERR_DROP,"CL_ParseServerMessage: read past end of server message");
    614 			break;
    615 		}
    616 
    617 		cmd = MSG_ReadByte( msg );
    618 
    619 		if ( cmd == svc_EOF) {
    620 			SHOWNET( msg, "END OF MESSAGE" );
    621 			break;
    622 		}
    623 
    624 		if ( cl_shownet->integer >= 2 ) {
    625 			if ( !svc_strings[cmd] ) {
    626 				Com_Printf( "%3i:BAD CMD %i\n", msg->readcount-1, cmd );
    627 			} else {
    628 				SHOWNET( msg, svc_strings[cmd] );
    629 			}
    630 		}
    631 	
    632 	// other commands
    633 		switch ( cmd ) {
    634 		default:
    635 			Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
    636 			break;			
    637 		case svc_nop:
    638 			break;
    639 		case svc_serverCommand:
    640 			CL_ParseCommandString( msg );
    641 			break;
    642 		case svc_gamestate:
    643 			CL_ParseGamestate( msg );
    644 			break;
    645 		case svc_snapshot:
    646 			CL_ParseSnapshot( msg );
    647 			break;
    648 		case svc_download:
    649 			CL_ParseDownload( msg );
    650 			break;
    651 		}
    652 	}
    653 }
    654 
    655