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 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include "client.h"
27 
28 char *svc_strings[256] =
29 {
30 	"svc_bad",
31 
32 	"svc_muzzleflash",
33 	"svc_muzzlflash2",
34 	"svc_temp_entity",
35 	"svc_layout",
36 	"svc_inventory",
37 
38 	"svc_nop",
39 	"svc_disconnect",
40 	"svc_reconnect",
41 	"svc_sound",
42 	"svc_print",
43 	"svc_stufftext",
44 	"svc_serverdata",
45 	"svc_configstring",
46 	"svc_spawnbaseline",
47 	"svc_centerprint",
48 	"svc_download",
49 	"svc_playerinfo",
50 	"svc_packetentities",
51 	"svc_deltapacketentities",
52 	"svc_frame"
53 };
54 
55 static size_t szr; // just for unused result warnings
56 
57 /*
58 ==============
59 Q_strncpyz
60 ==============
61 */
Q_strncpyz(char * dest,const char * src,size_t size)62 void Q_strncpyz( char *dest, const char *src, size_t size )
63 {
64 #if defined HAVE_STRLCPY
65 	strlcpy( dest, src, size );
66 #else
67 	if( size )
68 	{
69 		while( --size && (*dest++ = *src++) );
70 		*dest = '\0';
71 	}
72 #endif
73 }
74 
75 //=============================================================================
76 
CL_DownloadFileName(char * dest,int destlen,char * fn)77 void CL_DownloadFileName(char *dest, int destlen, char *fn)
78 {
79 	Com_sprintf (dest, destlen, "%s/%s", FS_Gamedir(), fn);
80 }
81 
82 /*
83 ===============
84 CL_CheckOrDownloadFile
85 
86 Returns true if the file exists, otherwise it attempts
87 to start a download from the server.
88 ===============
89 */
CL_CheckOrDownloadFile(char * filename)90 qboolean	CL_CheckOrDownloadFile (char *filename)
91 {
92 	FILE *fp;
93 	char	name[MAX_OSPATH];
94 	char	shortname[MAX_OSPATH];
95 	char    shortname2[MAX_OSPATH];
96 	qboolean	jpg = false;
97 
98 	if (strstr (filename, ".."))
99 	{
100 		Com_Printf ("Refusing to download a path with ..\n");
101 		return true;
102 	}
103 
104     //if pcx, strip extension and change to .jpg, we do not use .pcx anymore
105     if(filename[strlen(filename)-1] == 'x')
106 	{
107 		//Filter out any potentially screwed up texture paths(meshes only reside in these folders)
108 		if (strncmp(filename, "models", 6) && strncmp(filename, "vehicles", 8)
109 			&& strncmp(filename, "maps", 4))
110 			return true;
111 
112 		COM_StripExtension ( filename, shortname );
113 		sprintf(filename, "%s.jpg", shortname);
114 	}
115 
116 	//if jpg, be sure to also try tga
117     if(filename[strlen(filename)-2] == 'p' && filename[strlen(filename)-1] == 'g')
118 		jpg = true;
119 
120 	if (FS_LoadFile (filename, NULL) != -1)
121 	{
122 		// it exists, no need to download
123 		return true;
124 	}
125 
126 	if(jpg)
127 	{
128 		//didn't find .jpg skin, try for .tga skin
129 		//check for presence of a local .tga(but leave filename as original extension)
130 		//if we find a .tga, don't try to download anything
131 		COM_StripExtension ( filename, shortname );
132 		sprintf(shortname2, "%s.tga", shortname);
133 		if (FS_LoadFile (shortname2, NULL) != -1)
134 		{
135 			// it exists, no need to download
136 			return true;
137 		}
138 	}
139 
140 	strcpy (cls.downloadname, filename);
141 
142 	// download to a temp name, and only rename
143 	// to the real name when done, so if interrupted
144 	// a runt file wont be left
145 	COM_StripExtension (cls.downloadname, cls.downloadtempname);
146 	strcat (cls.downloadtempname, ".tmp");
147 
148 	// attempt an http download if available(never try to dl game model skins here)
149 	if(cls.downloadurl[0] && CL_HttpDownload())
150 			return false;
151 
152 //ZOID
153 	// check to see if we already have a tmp for this file, if so, try to resume
154 	// open the file if not opened yet
155 	CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
156 
157 //	FS_CreatePath (name);
158 
159 	fp = fopen (name, "r+b");
160 	if (fp)
161 	{ // it exists
162 		int len;
163 
164 		len = FS_filelength( fp );
165 		cls.download = fp;
166 
167 		// give the server an offset to start the download
168 		Com_Printf ("Resuming %s\n", cls.downloadname);
169 		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
170 		MSG_WriteString (&cls.netchan.message,
171 			va("download %s %i", cls.downloadname, len));
172 	} else
173 	{
174 		Com_Printf ("Downloading %s\n", cls.downloadname);
175 		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
176 		MSG_WriteString (&cls.netchan.message,
177 			va("download %s", cls.downloadname));
178 	}
179 
180 	cls.downloadnumber++;
181 
182 	send_packet_now = true;
183 	return false;
184 }
185 
186 /*
187 ===============
188 CL_Download_f
189 
190 Request a download from the server
191 ===============
192 */
CL_Download_f(void)193 void	CL_Download_f (void)
194 {
195 	char filename[MAX_OSPATH];
196 
197 	if (Cmd_Argc() != 2)
198 	{
199 		Com_Printf("Usage: download <filename>\n");
200 		return;
201 	}
202 
203 	Com_sprintf(filename, sizeof(filename), "%s", Cmd_Argv(1));
204 
205 	if (strstr (filename, ".."))
206 	{
207 		Com_Printf ("Refusing to download a path with ..\n");
208 		return;
209 	}
210 
211 	if (FS_LoadFile (filename, NULL) != -1)
212 	{	// it exists, no need to download
213 		Com_Printf("File already exists.\n");
214 		return;
215 	}
216 
217 	strcpy (cls.downloadname, filename);
218 	Com_Printf ("Downloading %s\n", cls.downloadname);
219 
220 	// download to a temp name, and only rename
221 	// to the real name when done, so if interrupted
222 	// a runt file wont be left
223 	COM_StripExtension (cls.downloadname, cls.downloadtempname);
224 	strcat (cls.downloadtempname, ".tmp");
225 
226 	MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
227 	MSG_WriteString (&cls.netchan.message,
228 		va("download %s", cls.downloadname));
229 
230 	cls.downloadnumber++;
231 }
232 
233 /*
234 ======================
235 CL_RegisterSounds
236 ======================
237 */
CL_RegisterSounds(void)238 void CL_RegisterSounds (void)
239 {
240 	int		i;
241 
242 	S_BeginRegistration ();
243 	CL_RegisterTEntSounds ();
244 	for (i=1 ; i<MAX_SOUNDS ; i++)
245 	{
246 		if (!cl.configstrings[CS_SOUNDS+i][0])
247 			break;
248 		cl.sound_precache[i] = S_RegisterSound (cl.configstrings[CS_SOUNDS+i]);
249 		Sys_SendKeyEvents ();	// pump message loop
250 	}
251 	S_EndRegistration ();
252 }
253 
254 
255 /*
256 =====================
257 CL_ParseDownload
258 
259 A download message has been received from the server
260 =====================
261 */
CL_ParseDownload(void)262 void CL_ParseDownload (void)
263 {
264 	int		size, percent;
265 	char	name[MAX_OSPATH];
266 	int		r;
267 
268 	// read the data
269 	size = MSG_ReadShort (&net_message);
270 	percent = MSG_ReadByte (&net_message);
271 	if (size < 0) //fix issues with bad data being dl'd
272 	{
273 		Com_Printf ("Server does not have file %s.\n", cls.downloadname);
274 
275 		//nuke the temp filename, we don't want that getting left around.
276 		cls.downloadtempname[0] = 0;
277 		cls.downloadname[0] = 0;
278 
279 		if (cls.download)
280 		{
281 			// if here, we tried to resume a file but the server said no
282 			fclose (cls.download);
283 			cls.download = NULL;
284 		}
285 		CL_RequestNextDownload ();
286 		return;
287 	}
288 
289 	// open the file if not opened yet
290 	if (!cls.download)
291 	{
292 		CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);
293 
294 		FS_CreatePath (name);
295 
296 		cls.download = fopen (name, "wb");
297 		if (!cls.download)
298 		{
299 			net_message.readcount += size;
300 			Com_Printf ("Failed to open %s\n", cls.downloadtempname);
301 			CL_RequestNextDownload ();
302 			return;
303 		}
304 	}
305 
306 	szr = fwrite (net_message.data + net_message.readcount, 1, size, cls.download);
307 	net_message.readcount += size;
308 
309 	if (percent != 100)
310 	{
311 		// request next block
312 		cls.downloadpercent = percent;
313 
314 		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
315 		SZ_Print (&cls.netchan.message, "nextdl");
316 		send_packet_now = true;
317 	}
318 	else
319 	{
320 		char	oldn[MAX_OSPATH];
321 		char	newn[MAX_OSPATH];
322 
323 //		Com_Printf ("100%%\n");
324 
325 		fclose (cls.download);
326 
327 		// rename the temp file to it's final name
328 		CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
329 		CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
330 		r = rename (oldn, newn);
331 		if (r)
332 			Com_Printf ("failed to rename.\n");
333 
334 		cls.download = NULL;
335 		cls.downloadpercent = 0;
336 
337 		// get another file if needed
338 
339 		CL_RequestNextDownload ();
340 	}
341 }
342 
343 
344 /*
345 =====================================================================
346 
347   SERVER CONNECTING MESSAGES
348 
349 =====================================================================
350 */
351 
352 /*
353 ==================
354 CL_ParseServerData
355 ==================
356 */
CL_ParseServerData(void)357 void CL_ParseServerData (void)
358 {
359 	 extern cvar_t	*fs_gamedirvar;
360 	char	*str;
361 	int		i;
362 
363 	Com_DPrintf ("Serverdata packet received.\n");
364 //
365 // wipe the client_state_t struct
366 //
367 	CL_ClearState ();
368 	cls.state = ca_connected;
369 
370 // parse protocol version number
371 	i = MSG_ReadLong (&net_message);
372 	cls.serverProtocol = i;
373 
374 	// BIG HACK to let demos from release work with the 3.0x patch!!!
375 	if (Com_ServerState() && PROTOCOL_VERSION == 34)
376 	{
377 	}
378 	else if (i != PROTOCOL_VERSION)
379 		Com_Error (ERR_DROP,"Server returned version %i, not %i", i, PROTOCOL_VERSION);
380 
381 	cl.servercount = MSG_ReadLong (&net_message);
382 	cl.attractloop = MSG_ReadByte (&net_message);
383 
384 	// Hide console for demo playback. It interferes with timedemo results and
385 	// is annoying when you just want to watch.
386 	if (cl.attractloop)
387 		M_ForceMenuOff ();
388 
389 	// game directory
390 	str = MSG_ReadString (&net_message);
391 	strncpy (cl.gamedir, str, sizeof(cl.gamedir)-1);
392 
393 	// set gamedir
394 	if ((*str && (!fs_gamedirvar->string || !*fs_gamedirvar->string || strcmp(fs_gamedirvar->string, str))) || (!*str && (fs_gamedirvar->string || *fs_gamedirvar->string)))
395 		Cvar_Set("game", str);
396 
397 	// parse player entity number
398 	cl.playernum = MSG_ReadShort (&net_message);
399 
400 	// get the full level name
401 	str = MSG_ReadString (&net_message);
402 
403 	// seperate the printfs so the server message can have a color
404 	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");
405 	Com_Printf ("%c%s\n", 2, str);
406 
407 	// need to prep refresh at next oportunity
408 	cl.refresh_prepped = false;
409 }
410 
411 /*
412 ==================
413 CL_ParseBaseline
414 ==================
415 */
CL_ParseBaseline(void)416 void CL_ParseBaseline (void)
417 {
418 	entity_state_t	*es;
419 	int				bits;
420 	int				newnum;
421 	entity_state_t	nullstate;
422 
423 	memset (&nullstate, 0, sizeof(nullstate));
424 
425 	newnum = CL_ParseEntityBits ( (unsigned *)&bits );
426 	es = &cl_entities[newnum].baseline;
427 	CL_ParseDelta (&nullstate, es, newnum, bits);
428 }
429 
430 
431 /*
432 ================
433 CL_LoadClientinfo
434 
435 ================
436 */
CL_LoadClientinfo(clientinfo_t * ci,char * s)437 void CL_LoadClientinfo (clientinfo_t *ci, char *s)
438 {
439 	int i;
440 	char		*t;
441 	char		model_name[MAX_QPATH];
442 	char		skin_name[MAX_QPATH];
443 	char		model_filename[MAX_QPATH];
444 	char		skin_filename[MAX_QPATH];
445 	char		weapon_filename[MAX_QPATH];
446 	FILE		*file;
447 
448 	strncpy(ci->cinfo, s, sizeof(ci->cinfo));
449 	ci->cinfo[sizeof(ci->cinfo)-1] = 0;
450 
451 	// isolate the player's name
452 	strncpy(ci->name, s, sizeof(ci->name));
453 	ci->name[sizeof(ci->name)-1] = 0;
454 	t = strstr (s, "\\");
455 	if (t)
456 	{
457 		ci->name[t-s] = 0;
458 		s = t+1;
459 	}
460 
461 	// isolate the model name
462 	strcpy (model_name, s);
463 	t = strstr(model_name, "/");
464 	if (!t)
465 		t = strstr(model_name, "\\");
466 	if (!t)
467 		t = model_name;
468 	*t = 0;
469 
470 	ci->helmet = NULL; //we only worry about this in these cases of missing textures or models
471 	ci->lod1 = NULL;
472 	ci->lod2 = NULL;
473 
474 	if (cl_noskins->value || *s == 0)
475 	{
476 		Com_sprintf (model_filename, sizeof(model_filename), "players/martianenforcer/tris.md2");
477 		Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/martianenforcer/weapon.md2");
478 		Com_sprintf (skin_filename, sizeof(skin_filename), "players/martianenforcer/default.pcx");
479 		Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/martianenforcer/default_i.pcx");
480 		ci->model = R_RegisterModel (model_filename);
481 		ci->helmet = R_RegisterModel("players/martianenforcer/helmet.md2");
482 		// weapon file
483 		for (i = 0; i < num_cl_weaponmodels; i++)
484 		{
485 			Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/martianenforcer/%s", cl_weaponmodels[i]);
486 			ci->weaponmodel[i] = R_RegisterModel(weapon_filename);
487 			if (!cl_vwep->value)
488 				break; // only one when vwep is off
489 		}
490 		ci->skin = R_RegisterSkin (skin_filename);
491 		ci->icon = R_RegisterPic (ci->iconname);
492 	}
493 	else
494 	{
495 		// isolate the skin name
496 		strcpy (skin_name, s + strlen(model_name) + 1);
497 
498 		// model file
499 		Com_sprintf (model_filename, sizeof(model_filename), "players/%s/tris.md2", model_name);
500 		ci->model = R_RegisterModel (model_filename);
501 		if (!ci->model)
502 		{
503 			Com_sprintf (model_filename, sizeof(model_filename), "players/martianenforcer/tris.md2");
504 			ci->model = R_RegisterModel (model_filename);
505 			ci->helmet = R_RegisterModel("players/martianenforcer/helmet.md2");
506 			strcpy(model_name, "martianenforcer");
507 		}
508 
509 		// skin file
510 		Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/%s.pcx", model_name, skin_name);
511 		ci->skin = R_RegisterSkin (skin_filename);
512 
513 		// if don't have a skin, it means that the model didn't have
514 		// it, so default
515 		if (!ci->skin)
516 		{
517 			strcpy(skin_name, "default");
518 			Com_sprintf (skin_filename, sizeof(skin_filename), "players/%s/default.pcx", model_name);
519 			ci->skin = R_RegisterSkin (skin_filename);
520 		}
521 
522 		// weapon file
523 		for (i = 0; i < num_cl_weaponmodels; i++)
524 		{
525 			Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/%s/%s", model_name, cl_weaponmodels[i]);
526 			ci->weaponmodel[i] = R_RegisterModel(weapon_filename);
527 			if (!ci->weaponmodel[i] == 0) {
528 				Com_sprintf (weapon_filename, sizeof(weapon_filename), "players/martianenforcer/%s", cl_weaponmodels[i]);
529 				ci->weaponmodel[i] = R_RegisterModel(weapon_filename);
530 			}
531 			if (!cl_vwep->value)
532 				break; // only one when vwep is off
533 		}
534 
535 		// icon file
536 		Com_sprintf (ci->iconname, sizeof(ci->iconname), "/players/%s/%s_i.pcx", model_name, skin_name);
537 		ci->icon = R_RegisterPic (ci->iconname);
538 	}
539 
540 	//check for level of detail models
541 	if (cl_noskins->value || *s == 0)
542 		strcpy(model_name, "martianenforcer");
543 
544 	Com_sprintf(model_filename, sizeof(model_filename), "players/%s/lod1.md2", model_name);
545 	i = 0;
546 	do
547 		model_filename[i] = tolower(model_filename[i]);
548 	while (model_filename[i++]);
549 
550 	FS_FOpenFile (model_filename, &file);
551 	if(file)
552 	{
553 		//exists
554 		fclose(file);
555 		ci->lod1 = R_RegisterModel(model_filename);
556 	}
557 	else
558 		ci->lod1 = NULL;
559 
560 	Com_sprintf(model_filename, sizeof(model_filename), "players/%s/lod2.md2", model_name);
561 	i = 0;
562 	do
563 		model_filename[i] = tolower(model_filename[i]);
564 	while (model_filename[i++]);
565 
566 	FS_FOpenFile (model_filename, &file);
567 	if(file)
568 	{
569 		//exists
570 		fclose(file);
571 		ci->lod2 = R_RegisterModel(model_filename);
572 	}
573 	else
574 		ci->lod2 = NULL;
575 
576 	// must have loaded all data types to be valid
577 	if (!ci->skin || !ci->icon || !ci->model || !ci->weaponmodel[0])
578 	{
579 		ci->skin = NULL;
580 		ci->icon = NULL;
581 		ci->model = NULL;
582 		ci->weaponmodel[0] = NULL;
583 		return;
584 	}
585 
586 }
587 
588 /*
589 ================
590 CL_ParseClientinfo
591 
592 Load the skin, icon, and model for a client
593 ================
594 */
CL_ParseClientinfo(int player)595 void CL_ParseClientinfo (int player)
596 {
597 	char			*s;
598 	clientinfo_t	*ci;
599 
600 	s = cl.configstrings[player+CS_PLAYERSKINS];
601 
602 	ci = &cl.clientinfo[player];
603 
604 	CL_LoadClientinfo (ci, s);
605 }
606 
CL_ParseTaunt(char * s)607 void CL_ParseTaunt( char *s)
608 {
609 	int l, j;
610 	char tauntsound[MAX_OSPATH];
611 
612 	//parse
613 	strcpy( scr_playericon, COM_Parse( &s ) );
614 	l = strlen(scr_playericon);
615 
616 	for (j=0 ; j<l ; j++)
617 		scr_playericon[j] = tolower(scr_playericon[j]);
618 
619 	Com_sprintf(scr_playericon, sizeof(scr_playericon), "%s_i", scr_playericon);
620 
621 	strcpy( tauntsound, COM_Parse( &s ) );
622 
623 	Q_strncpyz2( scr_playername, COM_Parse( &s ), sizeof(scr_playername) );
624 
625 	if(cl_playtaunts->value)
626 	{
627 		S_StartSound (NULL, 0, 0, S_RegisterSound (tauntsound), 1, ATTN_NONE, 0);
628 		scr_playericonalpha = 2.0;
629 	}
630 
631 }
632 
633 /*
634 ================
635 CL_ParseConfigString
636 ================
637 */
CL_ParseConfigString(void)638 void CL_ParseConfigString (void)
639 {
640 	int		i;
641 	char	*s;
642 	char	olds[MAX_QPATH];
643 	size_t	length;
644 
645 	i = MSG_ReadShort (&net_message);
646 	if (i < 0 || i >= MAX_CONFIGSTRINGS)
647 		Com_Error (ERR_DROP, "configstring > MAX_CONFIGSTRINGS");
648 	s = MSG_ReadString(&net_message);
649 
650 	strncpy (olds, cl.configstrings[i], sizeof(olds));
651 	olds[sizeof(olds) - 1] = 0;
652 
653 	//r1: overflow may be desired by some mods in stats programs for example. who knows.
654 	length = strlen(s);
655 
656 	if (length >= (sizeof(cl.configstrings[0]) * (MAX_CONFIGSTRINGS-i)) - 1)
657 		Com_Error (ERR_DROP, "CL_ParseConfigString: configstring %d exceeds available space", i);
658 
659 	//r1: don't allow basic things to overflow
660 	if (i != CS_NAME && i < CS_GENERAL)
661 	{
662 		if (i >= CS_STATUSBAR && i < CS_AIRACCEL)
663 		{
664 			strncpy (cl.configstrings[i], s, (sizeof(cl.configstrings[i]) * (CS_AIRACCEL - i))-1);
665 		}
666 		else
667 		{
668 			// Alien Arena client/server protocol depends on MAX_QPATH being 64
669 			if (length >= MAX_QPATH)
670 				Com_Printf ("WARNING: Configstring %d of length %d exceeds MAX_QPATH.\n", i, length);
671 			Q_strncpyz (cl.configstrings[i], s, sizeof(cl.configstrings[i])-1);
672 		}
673 	}
674 	else
675 	{
676 		strcpy (cl.configstrings[i], s);
677 	}
678 
679 	// do something apropriate
680 
681 	if (i >= CS_LIGHTS && i < CS_LIGHTS+MAX_LIGHTSTYLES)
682 		CL_SetLightstyle (i - CS_LIGHTS);
683 	else if (i >= CS_MODELS && i < CS_MODELS+MAX_MODELS)
684 	{
685 		if (cl.refresh_prepped)
686 		{
687 			cl.model_draw[i-CS_MODELS] = R_RegisterModel (cl.configstrings[i]);
688 			if (cl.configstrings[i][0] == '*')
689 				cl.model_clip[i-CS_MODELS] = CM_InlineModel (cl.configstrings[i]);
690 			else
691 				cl.model_clip[i-CS_MODELS] = NULL;
692 		}
693 	}
694 	else if (i >= CS_SOUNDS && i < CS_SOUNDS+MAX_MODELS)
695 	{
696 		if (cl.refresh_prepped)
697 			cl.sound_precache[i-CS_SOUNDS] = S_RegisterSound (cl.configstrings[i]);
698 	}
699 	else if (i >= CS_IMAGES && i < CS_IMAGES+MAX_MODELS)
700 	{
701 		if (cl.refresh_prepped)
702 			cl.image_precache[i-CS_IMAGES] = R_RegisterPic (cl.configstrings[i]);
703 	}
704 	else if (i >= CS_PLAYERSKINS && i < CS_PLAYERSKINS+MAX_CLIENTS)
705 	{
706 		if (cl.refresh_prepped && strcmp(olds, s))
707 			CL_ParseClientinfo (i-CS_PLAYERSKINS);
708 	}
709 	else if ( i == CS_GENERAL)
710 		CL_ParseTaunt(s);
711 }
712 
713 
714 /*
715 =====================================================================
716 
717 ACTION MESSAGES
718 
719 =====================================================================
720 */
721 
722 /*
723 ==================
724 CL_ParseStartSoundPacket
725 ==================
726 */
CL_ParseStartSoundPacket(void)727 void CL_ParseStartSoundPacket(void)
728 {
729     vec3_t  pos_v;
730 	float	*pos;
731     int 	channel, ent;
732     int 	sound_num;
733     float 	volume;
734     float 	attenuation;
735 	int		flags;
736 	float	ofs;
737 
738 	flags = MSG_ReadByte (&net_message);
739 	sound_num = MSG_ReadByte (&net_message);
740 
741     if (flags & SND_VOLUME)
742 		volume = MSG_ReadByte (&net_message) / 255.0;
743 	else
744 		volume = DEFAULT_SOUND_PACKET_VOLUME;
745 
746     if (flags & SND_ATTENUATION)
747 		attenuation = MSG_ReadByte (&net_message) / 64.0;
748 	else
749 		attenuation = DEFAULT_SOUND_PACKET_ATTENUATION;
750 
751     if (flags & SND_OFFSET)
752 		ofs = MSG_ReadByte (&net_message) / 1000.0;
753 	else
754 		ofs = 0;
755 
756 	if (flags & SND_ENT)
757 	{	// entity reletive
758 		channel = MSG_ReadShort(&net_message);
759 		ent = channel>>3;
760 		if (ent > MAX_EDICTS)
761 			Com_Error (ERR_DROP,"CL_ParseStartSoundPacket: ent = %i", ent);
762 
763 		channel &= 7;
764 	}
765 	else
766 	{
767 		ent = 0;
768 		channel = 0;
769 	}
770 
771 	if (flags & SND_POS)
772 	{	// positioned in space
773 		MSG_ReadPos (&net_message, pos_v);
774 
775 		pos = pos_v;
776 	}
777 	else	// use entity number
778 		pos = NULL;
779 
780 	if (!cl.sound_precache[sound_num])
781 		return;
782 
783 	S_StartSound (pos, ent, channel, cl.sound_precache[sound_num], volume, attenuation, ofs);
784 }
785 
786 
SHOWNET(char * s)787 void SHOWNET(char *s)
788 {
789 	if (cl_shownet->value>=2)
790 		Com_Printf ("%3i:%s\n", net_message.readcount-1, s);
791 }
792 
793 /*
794 =====================
795 CL_ParseServerMessage
796 =====================
797 */
CL_ParseServerMessage(void)798 void CL_ParseServerMessage (void)
799 {
800 	int			cmd;
801 	char		*s;
802 	int			i;
803 
804 //
805 // if recording demos, copy the message out
806 //
807 	if (cl_shownet->value == 1)
808 		Com_Printf ("%i ",net_message.cursize);
809 	else if (cl_shownet->value >= 2)
810 		Com_Printf ("------------------\n");
811 
812 //
813 // parse the message
814 //
815 	while (1)
816 	{
817 		if (net_message.readcount > net_message.cursize)
818 		{
819 			Com_Error (ERR_DROP,"CL_ParseServerMessage: Bad server message");
820 			break;
821 		}
822 
823 		cmd = MSG_ReadByte (&net_message);
824 
825 		if (cmd == -1)
826 		{
827 			SHOWNET("END OF MESSAGE");
828 			break;
829 		}
830 
831 		if (cl_shownet->value>=2)
832 		{
833 			if (!svc_strings[cmd])
834 				Com_Printf ("%3i:BAD CMD %i\n", net_message.readcount-1,cmd);
835 			else
836 				SHOWNET(svc_strings[cmd]);
837 		}
838 
839 	// other commands
840 		switch (cmd)
841 		{
842 		default:
843 			Com_Error (ERR_DROP,"CL_ParseServerMessage: Illegible server message\n");
844 			break;
845 
846 		case svc_nop:
847 //			Com_Printf ("svc_nop\n");
848 			break;
849 
850 		case svc_disconnect:
851 			Com_Error (ERR_DISCONNECT,"Server disconnected\n");
852 			break;
853 
854 		case svc_reconnect:
855 			Com_Printf ("Server disconnected, reconnecting\n");
856 			// stop download
857 				if(cls.download)
858 				{
859 					if(cls.downloadhttp)  // clean up http downloads
860 						CL_HttpDownloadCleanup();
861 					else  // or just stop legacy ones
862 						fclose(cls.download);
863 					cls.download = NULL;
864 				}
865 			cls.state = ca_connecting;
866 			cls.connect_time = -99999;	// CL_CheckForResend() will fire immediately
867 			break;
868 
869 		case svc_print:
870 			i = MSG_ReadByte (&net_message);
871 			if (i == PRINT_CHAT)
872 			{
873 				S_StartLocalSound ("misc/talk.wav");
874 			}
875 			Com_Printf ("%s", MSG_ReadString (&net_message));
876 			break;
877 
878 		case svc_centerprint:
879 			SCR_CenterPrint (MSG_ReadString (&net_message));
880 			break;
881 
882 		case svc_stufftext:
883 			s = MSG_ReadString (&net_message);
884 			Com_DPrintf ("stufftext: %s\n", s);
885 			Cbuf_AddText (s);
886 			break;
887 
888 		case svc_serverdata:
889 			Cbuf_Execute ();		// make sure any stuffed commands are done
890 			CL_ParseServerData ();
891 			break;
892 
893 		case svc_configstring:
894 			CL_ParseConfigString ();
895 			break;
896 
897 		case svc_sound:
898 			CL_ParseStartSoundPacket();
899 			break;
900 
901 		case svc_spawnbaseline:
902 			CL_ParseBaseline ();
903 			break;
904 
905 		case svc_temp_entity:
906 			CL_ParseTEnt ();
907 			break;
908 
909 		case svc_muzzleflash:
910 			CL_ParseMuzzleFlash ();
911 			break;
912 
913 		case svc_download:
914 			CL_ParseDownload ();
915 			break;
916 
917 		case svc_frame:
918 			CL_ParseFrame ();
919 			break;
920 
921 		case svc_inventory:
922 			CL_ParseInventory ();
923 			break;
924 
925 		case svc_layout:
926 			s = MSG_ReadString (&net_message);
927 			strncpy (cl.layout, s, sizeof(cl.layout)-1);
928 			break;
929 
930 		case svc_playerinfo:
931 		case svc_packetentities:
932 		case svc_deltapacketentities:
933 			Com_Error (ERR_DROP, "Out of place frame data");
934 			break;
935 		}
936 	}
937 
938 	CL_AddNetgraph ();
939 
940 	//
941 	// we don't know if it is ok to save a demo message until
942 	// after we have parsed the frame
943 	//
944 	if (cls.demorecording && !cls.demowaiting)
945 		CL_WriteDemoMessage ();
946 
947 }
948 
949 
950