Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

cl_parse.c (18380B)


      1 /*
      2 Copyright (C) 1997-2001 Id Software, Inc.
      3 
      4 This program is free software; you can redistribute it and/or
      5 modify it under the terms of the GNU General Public License
      6 as published by the Free Software Foundation; either version 2
      7 of the License, or (at your option) any later version.
      8 
      9 This program is distributed in the hope that it will be useful,
     10 but WITHOUT ANY WARRANTY; without even the implied warranty of
     11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
     12 
     13 See the GNU General Public License for more details.
     14 
     15 You should have received a copy of the GNU General Public License
     16 along with this program; if not, write to the Free Software
     17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     18 
     19 */
     20 // cl_parse.c  -- parse a message received from the server
     21 
     22 #include "client.h"
     23 
     24 char *svc_strings[256] =
     25 {
     26 	"svc_bad",
     27 
     28 	"svc_muzzleflash",
     29 	"svc_muzzlflash2",
     30 	"svc_temp_entity",
     31 	"svc_layout",
     32 	"svc_inventory",
     33 
     34 	"svc_nop",
     35 	"svc_disconnect",
     36 	"svc_reconnect",
     37 	"svc_sound",
     38 	"svc_print",
     39 	"svc_stufftext",
     40 	"svc_serverdata",
     41 	"svc_configstring",
     42 	"svc_spawnbaseline",	
     43 	"svc_centerprint",
     44 	"svc_download",
     45 	"svc_playerinfo",
     46 	"svc_packetentities",
     47 	"svc_deltapacketentities",
     48 	"svc_frame"
     49 };
     50 
     51 //=============================================================================
     52 
     53 void CL_DownloadFileName(char *dest, int destlen, char *fn)
     54 {
     55 	if (strncmp(fn, "players", 7) == 0)
     56 		Com_sprintf (dest, destlen, "%s/%s", BASEDIRNAME, fn);
     57 	else
     58 		Com_sprintf (dest, destlen, "%s/%s", FS_Gamedir(), fn);
     59 }
     60 
     61 /*
     62 ===============
     63 CL_CheckOrDownloadFile
     64 
     65 Returns true if the file exists, otherwise it attempts
     66 to start a download from the server.
     67 ===============
     68 */
     69 qboolean	CL_CheckOrDownloadFile (char *filename)
     70 {
     71 	FILE *fp;
     72 	char	name[MAX_OSPATH];
     73 
     74 	if (strstr (filename, ".."))
     75 	{
     76 		Com_Printf ("Refusing to download a path with ..\n");
     77 		return true;
     78 	}
     79 
     80 	if (FS_LoadFile (filename, NULL) != -1)
     81 	{	// it exists, no need to download
     82 		return true;
     83 	}
     84 
     85 	strcpy (cls.downloadname, filename);
     86 
     87 	// download to a temp name, and only rename
     88 	// to the real name when done, so if interrupted
     89 	// a runt file wont be left
     90 	COM_StripExtension (cls.downloadname, cls.downloadtempname);
     91 	strcat (cls.downloadtempname, ".tmp");
     92 
     93 //ZOID
     94 	// check to see if we already have a tmp for this file, if so, try to resume
     95 	// open the file if not opened yet
     96 	CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
     97 
     98 //	FS_CreatePath (name);
     99 
    100 	fp = fopen (name, "r+b");
    101 	if (fp) { // it exists
    102 		int len;
    103 		fseek(fp, 0, SEEK_END);
    104 		len = ftell(fp);
    105 
    106 		cls.download = fp;
    107 
    108 		// give the server an offset to start the download
    109 		Com_Printf ("Resuming %s\n", cls.downloadname);
    110 		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
    111 		MSG_WriteString (&cls.netchan.message,
    112 			va("download %s %i", cls.downloadname, len));
    113 	} else {
    114 		Com_Printf ("Downloading %s\n", cls.downloadname);
    115 		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
    116 		MSG_WriteString (&cls.netchan.message,
    117 			va("download %s", cls.downloadname));
    118 	}
    119 
    120 	cls.downloadnumber++;
    121 
    122 	return false;
    123 }
    124 
    125 /*
    126 ===============
    127 CL_Download_f
    128 
    129 Request a download from the server
    130 ===============
    131 */
    132 void	CL_Download_f (void)
    133 {
    134 	char filename[MAX_OSPATH];
    135 
    136 	if (Cmd_Argc() != 2) {
    137 		Com_Printf("Usage: download <filename>\n");
    138 		return;
    139 	}
    140 
    141 	Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1));
    142 
    143 	if (strstr (filename, ".."))
    144 	{
    145 		Com_Printf ("Refusing to download a path with ..\n");
    146 		return;
    147 	}
    148 
    149 	if (FS_LoadFile (filename, NULL) != -1)
    150 	{	// it exists, no need to download
    151 		Com_Printf("File already exists.\n");
    152 		return;
    153 	}
    154 
    155 	strcpy (cls.downloadname, filename);
    156 	Com_Printf ("Downloading %s\n", cls.downloadname);
    157 
    158 	// download to a temp name, and only rename
    159 	// to the real name when done, so if interrupted
    160 	// a runt file wont be left
    161 	COM_StripExtension (cls.downloadname, cls.downloadtempname);
    162 	strcat (cls.downloadtempname, ".tmp");
    163 
    164 	MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
    165 	MSG_WriteString (&cls.netchan.message,
    166 		va("download %s", cls.downloadname));
    167 
    168 	cls.downloadnumber++;
    169 }
    170 
    171 /*
    172 ======================
    173 CL_RegisterSounds
    174 ======================
    175 */
    176 void CL_RegisterSounds (void)
    177 {
    178 	int		i;
    179 
    180 	S_BeginRegistration ();
    181 	CL_RegisterTEntSounds ();
    182 	for (i=1 ; i<MAX_SOUNDS ; i++)
    183 	{
    184 		if (!cl.configstrings[CS_SOUNDS+i][0])
    185 			break;
    186 		cl.sound_precache[i] = S_RegisterSound (cl.configstrings[CS_SOUNDS+i]);
    187 		Sys_SendKeyEvents ();	// pump message loop
    188 	}
    189 	S_EndRegistration ();
    190 }
    191 
    192 
    193 /*
    194 =====================
    195 CL_ParseDownload
    196 
    197 A download message has been received from the server
    198 =====================
    199 */
    200 void CL_ParseDownload (void)
    201 {
    202 	int		size, percent;
    203 	char	name[MAX_OSPATH];
    204 	int		r;
    205 
    206 	// read the data
    207 	size = MSG_ReadShort (&net_message);
    208 	percent = MSG_ReadByte (&net_message);
    209 	if (size == -1)
    210 	{
    211 		Com_Printf ("Server does not have this file.\n");
    212 		if (cls.download)
    213 		{
    214 			// if here, we tried to resume a file but the server said no
    215 			fclose (cls.download);
    216 			cls.download = NULL;
    217 		}
    218 		CL_RequestNextDownload ();
    219 		return;
    220 	}
    221 
    222 	// open the file if not opened yet
    223 	if (!cls.download)
    224 	{
    225 		CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
    226 
    227 		FS_CreatePath (name);
    228 
    229 		cls.download = fopen (name, "wb");
    230 		if (!cls.download)
    231 		{
    232 			net_message.readcount += size;
    233 			Com_Printf ("Failed to open %s\n", cls.downloadtempname);
    234 			CL_RequestNextDownload ();
    235 			return;
    236 		}
    237 	}
    238 
    239 	fwrite (net_message.data + net_message.readcount, 1, size, cls.download);
    240 	net_message.readcount += size;
    241 
    242 	if (percent != 100)
    243 	{
    244 		// request next block
    245 // change display routines by zoid
    246 #if 0
    247 		Com_Printf (".");
    248 		if (10*(percent/10) != cls.downloadpercent)
    249 		{
    250 			cls.downloadpercent = 10*(percent/10);
    251 			Com_Printf ("%i%%", cls.downloadpercent);
    252 		}
    253 #endif
    254 		cls.downloadpercent = percent;
    255 
    256 		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
    257 		SZ_Print (&cls.netchan.message, "nextdl");
    258 	}
    259 	else
    260 	{
    261 		char	oldn[MAX_OSPATH];
    262 		char	newn[MAX_OSPATH];
    263 
    264 //		Com_Printf ("100%%\n");
    265 
    266 		fclose (cls.download);
    267 
    268 		// rename the temp file to it's final name
    269 		CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
    270 		CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
    271 		r = rename (oldn, newn);
    272 		if (r)
    273 			Com_Printf ("failed to rename.\n");
    274 
    275 		cls.download = NULL;
    276 		cls.downloadpercent = 0;
    277 
    278 		// get another file if needed
    279 
    280 		CL_RequestNextDownload ();
    281 	}
    282 }
    283 
    284 
    285 /*
    286 =====================================================================
    287 
    288   SERVER CONNECTING MESSAGES
    289 
    290 =====================================================================
    291 */
    292 
    293 /*
    294 ==================
    295 CL_ParseServerData
    296 ==================
    297 */
    298 void CL_ParseServerData (void)
    299 {
    300 	extern cvar_t	*fs_gamedirvar;
    301 	char	*str;
    302 	int		i;
    303 	
    304 	Com_DPrintf ("Serverdata packet received.\n");
    305 //
    306 // wipe the client_state_t struct
    307 //
    308 	CL_ClearState ();
    309 	cls.state = ca_connected;
    310 
    311 // parse protocol version number
    312 	i = MSG_ReadLong (&net_message);
    313 	cls.serverProtocol = i;
    314 
    315 	// BIG HACK to let demos from release work with the 3.0x patch!!!
    316 	if (Com_ServerState() && PROTOCOL_VERSION == 34)
    317 	{
    318 	}
    319 	else if (i != PROTOCOL_VERSION)
    320 		Com_Error (ERR_DROP,"Server returned version %i, not %i", i, PROTOCOL_VERSION);
    321 
    322 	cl.servercount = MSG_ReadLong (&net_message);
    323 	cl.attractloop = MSG_ReadByte (&net_message);
    324 
    325 	// game directory
    326 	str = MSG_ReadString (&net_message);
    327 	strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1);
    328 
    329 	// set gamedir
    330 	if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)))
    331 		Cvar_Set("game", str);
    332 
    333 	// parse player entity number
    334 	cl.playernum = MSG_ReadShort (&net_message);
    335 
    336 	// get the full level name
    337 	str = MSG_ReadString (&net_message);
    338 
    339 	if (cl.playernum == -1)
    340 	{	// playing a cinematic or showing a pic, not a level
    341 		SCR_PlayCinematic (str);
    342 	}
    343 	else
    344 	{
    345 		// seperate the printfs so the server message can have a color
    346 		Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
    347 		Com_Printf ("%c%s\n", 2, str);
    348 
    349 		// need to prep refresh at next oportunity
    350 		cl.refresh_prepped = false;
    351 	}
    352 }
    353 
    354 /*
    355 ==================
    356 CL_ParseBaseline
    357 ==================
    358 */
    359 void CL_ParseBaseline (void)
    360 {
    361 	entity_state_t	*es;
    362 	int				bits;
    363 	int				newnum;
    364 	entity_state_t	nullstate;
    365 
    366 	memset (&nullstate, 0, sizeof(nullstate));
    367 
    368 	newnum = CL_ParseEntityBits (&bits);
    369 	es = &cl_entities[newnum].baseline;
    370 	CL_ParseDelta (&nullstate, es, newnum, bits);
    371 }
    372 
    373 
    374 /*
    375 ================
    376 CL_LoadClientinfo
    377 
    378 ================
    379 */
    380 void CL_LoadClientinfo (clientinfo_t *ci, char *s)
    381 {
    382 	int i;
    383 	char		*t;
    384 	char		model_name[MAX_QPATH];
    385 	char		skin_name[MAX_QPATH];
    386 	char		model_filename[MAX_QPATH];
    387 	char		skin_filename[MAX_QPATH];
    388 	char		weapon_filename[MAX_QPATH];
    389 
    390 	strncpy(ci->cinfo, s, sizeof(ci->cinfo));
    391 	ci->cinfo[sizeof(ci->cinfo)-1] = 0;
    392 
    393 	// isolate the player's name
    394 	strncpy(ci->name, s, sizeof(ci->name));
    395 	ci->name[sizeof(ci->name)-1] = 0;
    396 	t = strstr (s, "\\");
    397 	if (t)
    398 	{
    399 		ci->name[t-s] = 0;
    400 		s = t+1;
    401 	}
    402 
    403 	if (cl_noskins->value || *s == 0)
    404 	{
    405 		Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
    406 		Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/weapon.md2");
    407 		Com_sprintf (skin_filename, sizeof(skin_filename), "players/male/grunt.pcx");
    408 		Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/male/grunt_i.pcx");
    409 		ci->model = re.RegisterModel (model_filename);
    410 		memset(ci->weaponmodel, 0, sizeof(ci->weaponmodel));
    411 		ci->weaponmodel[0] = re.RegisterModel (weapon_filename);
    412 		ci->skin = re.RegisterSkin (skin_filename);
    413 		ci->icon = re.RegisterPic (ci->iconname);
    414 	}
    415 	else
    416 	{
    417 		// isolate the model name
    418 		strcpy (model_name, s);
    419 		t = strstr(model_name, "/");
    420 		if (!t)
    421 			t = strstr(model_name, "\\");
    422 		if (!t)
    423 			t = model_name;
    424 		*t = 0;
    425 
    426 		// isolate the skin name
    427 		strcpy (skin_name, s + strlen(model_name) + 1);
    428 
    429 		// model file
    430 		Com_sprintf (model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name);
    431 		ci->model = re.RegisterModel (model_filename);
    432 		if (!ci->model)
    433 		{
    434 			strcpy(model_name, "male");
    435 			Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
    436 			ci->model = re.RegisterModel (model_filename);
    437 		}
    438 
    439 		// skin file
    440 		Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
    441 		ci->skin = re.RegisterSkin (skin_filename);
    442 
    443 		// if we don't have the skin and the model wasn't male,
    444 		// see if the male has it (this is for CTF's skins)
    445  		if (!ci->skin && Q_stricmp(model_name, "male"))
    446 		{
    447 			// change model to male
    448 			strcpy(model_name, "male");
    449 			Com_sprintf (model_filename, sizeof(model_filename), "players/male/tris.md2");
    450 			ci->model = re.RegisterModel (model_filename);
    451 
    452 			// see if the skin exists for the male model
    453 			Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
    454 			ci->skin = re.RegisterSkin (skin_filename);
    455 		}
    456 
    457 		// if we still don't have a skin, it means that the male model didn't have
    458 		// it, so default to grunt
    459 		if (!ci->skin) {
    460 			// see if the skin exists for the male model
    461 			Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/grunt.pcx", model_name, skin_name);
    462 			ci->skin = re.RegisterSkin (skin_filename);
    463 		}
    464 
    465 		// weapon file
    466 		for (i = 0; i < num_cl_weaponmodels; i++) {
    467 			Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]);
    468 			ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
    469 			if (!ci->weaponmodel[i] && strcmp(model_name, "cyborg") == 0) {
    470 				// try male
    471 				Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/male/%s", cl_weaponmodels[i]);
    472 				ci->weaponmodel[i] = re.RegisterModel(weapon_filename);
    473 			}
    474 			if (!cl_vwep->value)
    475 				break; // only one when vwep is off
    476 		}
    477 
    478 		// icon file
    479 		Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name);
    480 		ci->icon = re.RegisterPic (ci->iconname);
    481 	}
    482 
    483 	// must have loaded all data types to be valud
    484 	if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0])
    485 	{
    486 		ci->skin = NULL;
    487 		ci->icon = NULL;
    488 		ci->model = NULL;
    489 		ci->weaponmodel[0] = NULL;
    490 		return;
    491 	}
    492 }
    493 
    494 /*
    495 ================
    496 CL_ParseClientinfo
    497 
    498 Load the skin, icon, and model for a client
    499 ================
    500 */
    501 void CL_ParseClientinfo (int player)
    502 {
    503 	char			*s;
    504 	clientinfo_t	*ci;
    505 
    506 	s = cl.configstrings[player+CS_PLAYERSKINS];
    507 
    508 	ci = &cl.clientinfo[player];
    509 
    510 	CL_LoadClientinfo (ci, s);
    511 }
    512 
    513 
    514 /*
    515 ================
    516 CL_ParseConfigString
    517 ================
    518 */
    519 void CL_ParseConfigString (void)
    520 {
    521 	int		i;
    522 	char	*s;
    523 
    524 	i = MSG_ReadShort (&net_message);
    525 	if (i < 0 || i >= MAX_CONFIGSTRINGS)
    526 		Com_Error (ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
    527 	s = MSG_ReadString(&net_message);
    528 	strcpy (cl.configstrings[i], s);
    529 
    530 	// do something apropriate 
    531 
    532 	if (i >= CS_LIGHTS && i < CS_LIGHTS+MAX_LIGHTSTYLES)
    533 		CL_SetLightstyle (i - CS_LIGHTS);
    534 	else if (i == CS_CDTRACK)
    535 	{
    536 		if (cl.refresh_prepped)
    537 			CDAudio_Play (atoi(cl.configstrings[CS_CDTRACK]), true);
    538 	}
    539 	else if (i >= CS_MODELS && i < CS_MODELS+MAX_MODELS)
    540 	{
    541 		if (cl.refresh_prepped)
    542 		{
    543 			cl.model_draw[i-CS_MODELS] = re.RegisterModel (cl.configstrings[i]);
    544 			if (cl.configstrings[i][0] == '*')
    545 				cl.model_clip[i-CS_MODELS] = CM_InlineModel (cl.configstrings[i]);
    546 			else
    547 				cl.model_clip[i-CS_MODELS] = NULL;
    548 		}
    549 	}
    550 	else if (i >= CS_SOUNDS && i < CS_SOUNDS+MAX_MODELS)
    551 	{
    552 		if (cl.refresh_prepped)
    553 			cl.sound_precache[i-CS_SOUNDS] = S_RegisterSound (cl.configstrings[i]);
    554 	}
    555 	else if (i >= CS_IMAGES && i < CS_IMAGES+MAX_MODELS)
    556 	{
    557 		if (cl.refresh_prepped)
    558 			cl.image_precache[i-CS_IMAGES] = re.RegisterPic (cl.configstrings[i]);
    559 	}
    560 	else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS+MAX_CLIENTS)
    561 	{
    562 		if (cl.refresh_prepped)
    563 			CL_ParseClientinfo (i-CS_PLAYERSKINS);
    564 	}
    565 }
    566 
    567 
    568 /*
    569 =====================================================================
    570 
    571 ACTION MESSAGES
    572 
    573 =====================================================================
    574 */
    575 
    576 /*
    577 ==================
    578 CL_ParseStartSoundPacket
    579 ==================
    580 */
    581 void CL_ParseStartSoundPacket(void)
    582 {
    583     vec3_t  pos_v;
    584 	float	*pos;
    585     int 	channel, ent;
    586     int 	sound_num;
    587     float 	volume;
    588     float 	attenuation;  
    589 	int		flags;
    590 	float	ofs;
    591 
    592 	flags = MSG_ReadByte (&net_message);
    593 	sound_num = MSG_ReadByte (&net_message);
    594 
    595     if (flags & SND_VOLUME)
    596 		volume = MSG_ReadByte (&net_message) / 255.0;
    597 	else
    598 		volume = DEFAULT_SOUND_PACKET_VOLUME;
    599 	
    600     if (flags & SND_ATTENUATION)
    601 		attenuation = MSG_ReadByte (&net_message) / 64.0;
    602 	else
    603 		attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;	
    604 
    605     if (flags & SND_OFFSET)
    606 		ofs = MSG_ReadByte (&net_message) / 1000.0;
    607 	else
    608 		ofs = 0;
    609 
    610 	if (flags & SND_ENT)
    611 	{	// entity reletive
    612 		channel = MSG_ReadShort(&net_message); 
    613 		ent = channel>>3;
    614 		if (ent > MAX_EDICTS)
    615 			Com_Error (ERR_DROP,"CL_ParseStartSoundPacket: ent = %i", ent);
    616 
    617 		channel &= 7;
    618 	}
    619 	else
    620 	{
    621 		ent = 0;
    622 		channel = 0;
    623 	}
    624 
    625 	if (flags & SND_POS)
    626 	{	// positioned in space
    627 		MSG_ReadPos (&net_message, pos_v);
    628  
    629 		pos = pos_v;
    630 	}
    631 	else	// use entity number
    632 		pos = NULL;
    633 
    634 	if (!cl.sound_precache[sound_num])
    635 		return;
    636 
    637 	S_StartSound (pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs);
    638 }       
    639 
    640 
    641 void SHOWNET(char *s)
    642 {
    643 	if (cl_shownet->value>=2)
    644 		Com_Printf ("%3i:%s\n", net_message.readcount-1, s);
    645 }
    646 
    647 /*
    648 =====================
    649 CL_ParseServerMessage
    650 =====================
    651 */
    652 void CL_ParseServerMessage (void)
    653 {
    654 	int			cmd;
    655 	char		*s;
    656 	int			i;
    657 
    658 //
    659 // if recording demos, copy the message out
    660 //
    661 	if (cl_shownet->value == 1)
    662 		Com_Printf ("%i ",net_message.cursize);
    663 	else if (cl_shownet->value >= 2)
    664 		Com_Printf ("------------------\n");
    665 
    666 
    667 //
    668 // parse the message
    669 //
    670 	while (1)
    671 	{
    672 		if (net_message.readcount > net_message.cursize)
    673 		{
    674 			Com_Error (ERR_DROP,"CL_ParseServerMessage: Bad server message");
    675 			break;
    676 		}
    677 
    678 		cmd = MSG_ReadByte (&net_message);
    679 
    680 		if (cmd == -1)
    681 		{
    682 			SHOWNET("END OF MESSAGE");
    683 			break;
    684 		}
    685 
    686 		if (cl_shownet->value>=2)
    687 		{
    688 			if (!svc_strings[cmd])
    689 				Com_Printf ("%3i:BAD CMD %i\n", net_message.readcount-1,cmd);
    690 			else
    691 				SHOWNET(svc_strings[cmd]);
    692 		}
    693 	
    694 	// other commands
    695 		switch (cmd)
    696 		{
    697 		default:
    698 			Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
    699 			break;
    700 			
    701 		case svc_nop:
    702 //			Com_Printf ("svc_nop\n");
    703 			break;
    704 			
    705 		case svc_disconnect:
    706 			Com_Error (ERR_DISCONNECT,"Server disconnected\n");
    707 			break;
    708 
    709 		case svc_reconnect:
    710 			Com_Printf ("Server disconnected, reconnecting\n");
    711 			if (cls.download) {
    712 				//ZOID, close download
    713 				fclose (cls.download);
    714 				cls.download = NULL;
    715 			}
    716 			cls.state = ca_connecting;
    717 			cls.connect_time = -99999;	// CL_CheckForResend() will fire immediately
    718 			break;
    719 
    720 		case svc_print:
    721 			i = MSG_ReadByte (&net_message);
    722 			if (i == PRINT_CHAT)
    723 			{
    724 				S_StartLocalSound ("misc/talk.wav");
    725 				con.ormask = 128;
    726 			}
    727 			Com_Printf ("%s", MSG_ReadString (&net_message));
    728 			con.ormask = 0;
    729 			break;
    730 			
    731 		case svc_centerprint:
    732 			SCR_CenterPrint (MSG_ReadString (&net_message));
    733 			break;
    734 			
    735 		case svc_stufftext:
    736 			s = MSG_ReadString (&net_message);
    737 			Com_DPrintf ("stufftext: %s\n", s);
    738 			Cbuf_AddText (s);
    739 			break;
    740 			
    741 		case svc_serverdata:
    742 			Cbuf_Execute ();		// make sure any stuffed commands are done
    743 			CL_ParseServerData ();
    744 			break;
    745 			
    746 		case svc_configstring:
    747 			CL_ParseConfigString ();
    748 			break;
    749 			
    750 		case svc_sound:
    751 			CL_ParseStartSoundPacket();
    752 			break;
    753 			
    754 		case svc_spawnbaseline:
    755 			CL_ParseBaseline ();
    756 			break;
    757 
    758 		case svc_temp_entity:
    759 			CL_ParseTEnt ();
    760 			break;
    761 
    762 		case svc_muzzleflash:
    763 			CL_ParseMuzzleFlash ();
    764 			break;
    765 
    766 		case svc_muzzleflash2:
    767 			CL_ParseMuzzleFlash2 ();
    768 			break;
    769 
    770 		case svc_download:
    771 			CL_ParseDownload ();
    772 			break;
    773 
    774 		case svc_frame:
    775 			CL_ParseFrame ();
    776 			break;
    777 
    778 		case svc_inventory:
    779 			CL_ParseInventory ();
    780 			break;
    781 
    782 		case svc_layout:
    783 			s = MSG_ReadString (&net_message);
    784 			strncpy (cl.layout, s, sizeof(cl.layout)-1);
    785 			break;
    786 
    787 		case svc_playerinfo:
    788 		case svc_packetentities:
    789 		case svc_deltapacketentities:
    790 			Com_Error (ERR_DROP, "Out of place frame data");
    791 			break;
    792 		}
    793 	}
    794 
    795 	CL_AddNetgraph ();
    796 
    797 	//
    798 	// we don't know if it is ok to save a demo message until
    799 	// after we have parsed the frame
    800 	//
    801 	if (cls.demorecording && !cls.demowaiting)
    802 		CL_WriteDemoMessage ();
    803 
    804 }
    805 
    806