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 Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 ===========================================================================
21 */
22 // cl_main.c  -- client main loop
23 
24 #include "client.h"
25 #include <limits.h>
26 
27 #ifdef USE_MUMBLE
28 #include "libmumblelink.h"
29 #endif
30 
31 #ifdef USE_MUMBLE
32 cvar_t	*cl_useMumble;
33 cvar_t	*cl_mumbleScale;
34 #endif
35 
36 #ifdef USE_VOIP
37 cvar_t	*cl_voipUseVAD;
38 cvar_t	*cl_voipVADThreshold;
39 cvar_t	*cl_voipSend;
40 cvar_t	*cl_voipSendTarget;
41 cvar_t	*cl_voipGainDuringCapture;
42 cvar_t	*cl_voipCaptureMult;
43 cvar_t	*cl_voipShowMeter;
44 cvar_t	*cl_voip;
45 #endif
46 
47 cvar_t	*cl_nodelta;
48 cvar_t	*cl_debugMove;
49 
50 cvar_t	*cl_noprint;
51 cvar_t	*cl_motd;
52 
53 cvar_t	*rcon_client_password;
54 cvar_t	*rconAddress;
55 
56 cvar_t	*cl_timeout;
57 cvar_t	*cl_maxpackets;
58 cvar_t	*cl_packetdup;
59 cvar_t	*cl_timeNudge;
60 cvar_t	*cl_showTimeDelta;
61 cvar_t	*cl_freezeDemo;
62 
63 cvar_t	*cl_shownet;
64 cvar_t	*cl_showSend;
65 cvar_t	*cl_timedemo;
66 cvar_t	*cl_timedemoLog;
67 cvar_t	*cl_autoRecordDemo;
68 cvar_t	*cl_aviFrameRate;
69 cvar_t	*cl_aviMotionJpeg;
70 cvar_t	*cl_forceavidemo;
71 
72 cvar_t	*cl_freelook;
73 cvar_t	*cl_sensitivity;
74 
75 cvar_t	*cl_mouseAccel;
76 cvar_t	*cl_mouseAccelOffset;
77 cvar_t	*cl_mouseAccelStyle;
78 cvar_t	*cl_showMouseRate;
79 
80 cvar_t	*m_pitch;
81 cvar_t	*m_yaw;
82 cvar_t	*m_forward;
83 cvar_t	*m_side;
84 cvar_t	*m_filter;
85 
86 cvar_t	*cl_activeAction;
87 
88 cvar_t	*cl_motdString;
89 
90 cvar_t	*cl_allowDownload;
91 cvar_t	*cl_conXOffset;
92 cvar_t	*cl_inGameVideo;
93 
94 cvar_t	*cl_serverStatusResendTime;
95 cvar_t	*cl_trn;
96 
97 cvar_t	*cl_lanForcePackets;
98 
99 cvar_t	*cl_guidServerUniq;
100 
101 cvar_t	*cl_consoleKeys;
102 
103 cvar_t  *cl_consoleType;
104 cvar_t  *cl_consoleColor[4];
105 
106 cvar_t *cl_consoleHeight;
107 
108 cvar_t  *cl_gamename;
109 
110 clientActive_t		cl;
111 clientConnection_t	clc;
112 clientStatic_t		cls;
113 vm_t				*cgvm;
114 
115 // Structure containing functions exported from refresh DLL
116 refexport_t	re;
117 
118 ping_t	cl_pinglist[MAX_PINGREQUESTS];
119 
120 typedef struct serverStatus_s
121 {
122 	char string[BIG_INFO_STRING];
123 	netadr_t address;
124 	int time, startTime;
125 	qboolean pending;
126 	qboolean print;
127 	qboolean retrieved;
128 } serverStatus_t;
129 
130 serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS];
131 int serverStatusCount;
132 
133 #if defined __USEA3D && defined __A3D_GEOM
134 	void hA3Dg_ExportRenderGeom (refexport_t *incoming_re);
135 #endif
136 
137 extern void GLimp_Minimize(void);
138 extern void SV_BotFrame( int time );
139 void CL_CheckForResend( void );
140 void CL_ShowIP_f(void);
141 void CL_ServerStatus_f(void);
142 void CL_ServerStatusResponse( netadr_t from, msg_t *msg );
143 
144 /*
145 ===============
146 CL_CDDialog
147 
148 Called by Com_Error when a cd is needed
149 ===============
150 */
CL_CDDialog(void)151 void CL_CDDialog( void ) {
152 	cls.cddialog = qtrue;	// start it next frame
153 }
154 
155 #ifdef USE_MUMBLE
156 static
CL_UpdateMumble(void)157 void CL_UpdateMumble(void)
158 {
159 	vec3_t pos, forward, up;
160 	float scale = cl_mumbleScale->value;
161 	float tmp;
162 
163 	if(!cl_useMumble->integer)
164 		return;
165 
166 	// !!! FIXME: not sure if this is even close to correct.
167 	AngleVectors( cl.snap.ps.viewangles, forward, NULL, up);
168 
169 	pos[0] = cl.snap.ps.origin[0] * scale;
170 	pos[1] = cl.snap.ps.origin[2] * scale;
171 	pos[2] = cl.snap.ps.origin[1] * scale;
172 
173 	tmp = forward[1];
174 	forward[1] = forward[2];
175 	forward[2] = tmp;
176 
177 	tmp = up[1];
178 	up[1] = up[2];
179 	up[2] = tmp;
180 
181 	if(cl_useMumble->integer > 1) {
182 		fprintf(stderr, "%f %f %f, %f %f %f, %f %f %f\n",
183 			pos[0], pos[1], pos[2],
184 			forward[0], forward[1], forward[2],
185 			up[0], up[1], up[2]);
186 	}
187 
188 	mumble_update_coordinates(pos, forward, up);
189 }
190 #endif
191 
192 
193 #ifdef USE_VOIP
194 static
CL_UpdateVoipIgnore(const char * idstr,qboolean ignore)195 void CL_UpdateVoipIgnore(const char *idstr, qboolean ignore)
196 {
197 	if ((*idstr >= '0') && (*idstr <= '9')) {
198 		const int id = atoi(idstr);
199 		if ((id >= 0) && (id < MAX_CLIENTS)) {
200 			clc.voipIgnore[id] = ignore;
201 			CL_AddReliableCommand(va("voip %s %d",
202 			                         ignore ? "ignore" : "unignore", id), qfalse);
203 			Com_Printf("VoIP: %s ignoring player #%d\n",
204 			            ignore ? "Now" : "No longer", id);
205 			return;
206 		}
207 	}
208 	Com_Printf("VoIP: invalid player ID#\n");
209 }
210 
211 static
CL_UpdateVoipGain(const char * idstr,float gain)212 void CL_UpdateVoipGain(const char *idstr, float gain)
213 {
214 	if ((*idstr >= '0') && (*idstr <= '9')) {
215 		const int id = atoi(idstr);
216 		if (gain < 0.0f)
217 			gain = 0.0f;
218 		if ((id >= 0) && (id < MAX_CLIENTS)) {
219 			clc.voipGain[id] = gain;
220 			Com_Printf("VoIP: player #%d gain now set to %f\n", id, gain);
221 		}
222 	}
223 }
224 
CL_Voip_f(void)225 void CL_Voip_f( void )
226 {
227 	const char *cmd = Cmd_Argv(1);
228 	const char *reason = NULL;
229 
230 	if (cls.state != CA_ACTIVE)
231 		reason = "Not connected to a server";
232 	else if (!clc.speexInitialized)
233 		reason = "Speex not initialized";
234 	else if (!cl_connectedToVoipServer)
235 		reason = "Server doesn't support VoIP";
236 	else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
237 		reason = "running in single-player mode";
238 
239 	if (reason != NULL) {
240 		Com_Printf("VoIP: command ignored: %s\n", reason);
241 		return;
242 	}
243 
244 	if (strcmp(cmd, "ignore") == 0) {
245 		CL_UpdateVoipIgnore(Cmd_Argv(2), qtrue);
246 	} else if (strcmp(cmd, "unignore") == 0) {
247 		CL_UpdateVoipIgnore(Cmd_Argv(2), qfalse);
248 	} else if (strcmp(cmd, "gain") == 0) {
249 		if (Cmd_Argc() > 3) {
250 			CL_UpdateVoipGain(Cmd_Argv(2), atof(Cmd_Argv(3)));
251 		} else if (Q_isanumber(Cmd_Argv(2))) {
252 			int id = atoi(Cmd_Argv(2));
253 			if (id >= 0 && id < MAX_CLIENTS) {
254 				Com_Printf("VoIP: current gain for player #%d "
255 					"is %f\n", id, clc.voipGain[id]);
256 			} else {
257 				Com_Printf("VoIP: invalid player ID#\n");
258 			}
259 		} else {
260 			Com_Printf("usage: voip gain <playerID#> [value]\n");
261 		}
262 	} else if (strcmp(cmd, "muteall") == 0) {
263 		Com_Printf("VoIP: muting incoming voice\n");
264 		CL_AddReliableCommand("voip muteall", qfalse);
265 		clc.voipMuteAll = qtrue;
266 	} else if (strcmp(cmd, "unmuteall") == 0) {
267 		Com_Printf("VoIP: unmuting incoming voice\n");
268 		CL_AddReliableCommand("voip unmuteall", qfalse);
269 		clc.voipMuteAll = qfalse;
270 	} else {
271 		Com_Printf("usage: voip [un]ignore <playerID#>\n"
272 		           "       voip [un]muteall\n"
273 		           "       voip gain <playerID#> [value]\n");
274 	}
275 }
276 
277 
278 static
CL_VoipNewGeneration(void)279 void CL_VoipNewGeneration(void)
280 {
281 	// don't have a zero generation so new clients won't match, and don't
282 	//  wrap to negative so MSG_ReadLong() doesn't "fail."
283 	clc.voipOutgoingGeneration++;
284 	if (clc.voipOutgoingGeneration <= 0)
285 		clc.voipOutgoingGeneration = 1;
286 	clc.voipPower = 0.0f;
287 	clc.voipOutgoingSequence = 0;
288 }
289 
290 /*
291 ===============
292 CL_CaptureVoip
293 
294 Record more audio from the hardware if required and encode it into Speex
295  data for later transmission.
296 ===============
297 */
298 static
CL_CaptureVoip(void)299 void CL_CaptureVoip(void)
300 {
301 	const float audioMult = cl_voipCaptureMult->value;
302 	const qboolean useVad = (cl_voipUseVAD->integer != 0);
303 	qboolean initialFrame = qfalse;
304 	qboolean finalFrame = qfalse;
305 
306 #if USE_MUMBLE
307 	// if we're using Mumble, don't try to handle VoIP transmission ourselves.
308 	if (cl_useMumble->integer)
309 		return;
310 #endif
311 
312 	if (!clc.speexInitialized)
313 		return;  // just in case this gets called at a bad time.
314 
315 	if (clc.voipOutgoingDataSize > 0)
316 		return;  // packet is pending transmission, don't record more yet.
317 
318 	if (cl_voipUseVAD->modified) {
319 		Cvar_Set("cl_voipSend", (useVad) ? "1" : "0");
320 		cl_voipUseVAD->modified = qfalse;
321 	}
322 
323 	if ((useVad) && (!cl_voipSend->integer))
324 		Cvar_Set("cl_voipSend", "1");  // lots of things reset this.
325 
326 	if (cl_voipSend->modified) {
327 		qboolean dontCapture = qfalse;
328 		if (cls.state != CA_ACTIVE)
329 			dontCapture = qtrue;  // not connected to a server.
330 		else if (!cl_connectedToVoipServer)
331 			dontCapture = qtrue;  // server doesn't support VoIP.
332 		else if ( Cvar_VariableValue( "g_gametype" ) == GT_SINGLE_PLAYER || Cvar_VariableValue("ui_singlePlayerActive"))
333 			dontCapture = qtrue;  // single player game.
334 		else if (clc.demoplaying)
335 			dontCapture = qtrue;  // playing back a demo.
336 		else if ( cl_voip->integer == 0 )
337 			dontCapture = qtrue;  // client has VoIP support disabled.
338 		else if ( audioMult == 0.0f )
339 			dontCapture = qtrue;  // basically silenced incoming audio.
340 
341 		cl_voipSend->modified = qfalse;
342 
343 		if (dontCapture) {
344 			cl_voipSend->integer = 0;
345 			return;
346 		}
347 
348 		if (cl_voipSend->integer) {
349 			initialFrame = qtrue;
350 		} else {
351 			finalFrame = qtrue;
352 		}
353 	}
354 
355 	// try to get more audio data from the sound card...
356 
357 	if (initialFrame) {
358 		float gain = cl_voipGainDuringCapture->value;
359 		if (gain < 0.0f) gain = 0.0f; else if (gain >= 1.0f) gain = 1.0f;
360 		S_MasterGain(cl_voipGainDuringCapture->value);
361 		S_StartCapture();
362 		CL_VoipNewGeneration();
363 	}
364 
365 	if ((cl_voipSend->integer) || (finalFrame)) { // user wants to capture audio?
366 		int samples = S_AvailableCaptureSamples();
367 		const int mult = (finalFrame) ? 1 : 12; // 12 == 240ms of audio.
368 
369 		// enough data buffered in audio hardware to process yet?
370 		if (samples >= (clc.speexFrameSize * mult)) {
371 			// audio capture is always MONO16 (and that's what speex wants!).
372 			//  2048 will cover 12 uncompressed frames in narrowband mode.
373 			static int16_t sampbuffer[2048];
374 			float voipPower = 0.0f;
375 			int speexFrames = 0;
376 			int wpos = 0;
377 			int pos = 0;
378 
379 			if (samples > (clc.speexFrameSize * 12))
380 				samples = (clc.speexFrameSize * 12);
381 
382 			// !!! FIXME: maybe separate recording from encoding, so voipPower
383 			// !!! FIXME:  updates faster than 4Hz?
384 
385 			samples -= samples % clc.speexFrameSize;
386 			S_Capture(samples, (byte *) sampbuffer);  // grab from audio card.
387 
388 			// this will probably generate multiple speex packets each time.
389 			while (samples > 0) {
390 				int16_t *sampptr = &sampbuffer[pos];
391 				int i, bytes;
392 
393 				// preprocess samples to remove noise...
394 				speex_preprocess_run(clc.speexPreprocessor, sampptr);
395 
396 				// check the "power" of this packet...
397 				for (i = 0; i < clc.speexFrameSize; i++) {
398 					const float flsamp = (float) sampptr[i];
399 					const float s = fabs(flsamp);
400 					voipPower += s * s;
401 					sampptr[i] = (int16_t) ((flsamp) * audioMult);
402 				}
403 
404 				// encode raw audio samples into Speex data...
405 				speex_bits_reset(&clc.speexEncoderBits);
406 				speex_encode_int(clc.speexEncoder, sampptr,
407 				                 &clc.speexEncoderBits);
408 				bytes = speex_bits_write(&clc.speexEncoderBits,
409 				                         (char *) &clc.voipOutgoingData[wpos+1],
410 				                         sizeof (clc.voipOutgoingData) - (wpos+1));
411 				assert((bytes > 0) && (bytes < 256));
412 				clc.voipOutgoingData[wpos] = (byte) bytes;
413 				wpos += bytes + 1;
414 
415 				// look at the data for the next packet...
416 				pos += clc.speexFrameSize;
417 				samples -= clc.speexFrameSize;
418 				speexFrames++;
419 			}
420 
421 			clc.voipPower = (voipPower / (32768.0f * 32768.0f *
422 			                 ((float) (clc.speexFrameSize * speexFrames)))) *
423 			                 100.0f;
424 
425 			if ((useVad) && (clc.voipPower < cl_voipVADThreshold->value)) {
426 				CL_VoipNewGeneration();  // no "talk" for at least 1/4 second.
427 			} else {
428 				clc.voipOutgoingDataSize = wpos;
429 				clc.voipOutgoingDataFrames = speexFrames;
430 
431 				Com_DPrintf("VoIP: Send %d frames, %d bytes, %f power\n",
432 				            speexFrames, wpos, clc.voipPower);
433 
434 				#if 0
435 				static FILE *encio = NULL;
436 				if (encio == NULL) encio = fopen("voip-outgoing-encoded.bin", "wb");
437 				if (encio != NULL) { fwrite(clc.voipOutgoingData, wpos, 1, encio); fflush(encio); }
438 				static FILE *decio = NULL;
439 				if (decio == NULL) decio = fopen("voip-outgoing-decoded.bin", "wb");
440 				if (decio != NULL) { fwrite(sampbuffer, speexFrames * clc.speexFrameSize * 2, 1, decio); fflush(decio); }
441 				#endif
442 			}
443 		}
444 	}
445 
446 	// User requested we stop recording, and we've now processed the last of
447 	//  any previously-buffered data. Pause the capture device, etc.
448 	if (finalFrame) {
449 		S_StopCapture();
450 		S_MasterGain(1.0f);
451 		clc.voipPower = 0.0f;  // force this value so it doesn't linger.
452 	}
453 }
454 #endif
455 
456 /*
457 =======================================================================
458 
459 CLIENT RELIABLE COMMAND COMMUNICATION
460 
461 =======================================================================
462 */
463 
464 /*
465 ======================
466 CL_AddReliableCommand
467 
468 The given command will be transmitted to the server, and is gauranteed to
469 not have future usercmd_t executed before it is executed
470 ======================
471 */
CL_AddReliableCommand(const char * cmd,qboolean isDisconnectCmd)472 void CL_AddReliableCommand(const char *cmd, qboolean isDisconnectCmd)
473 {
474 	int unacknowledged = clc.reliableSequence - clc.reliableAcknowledge;
475 
476 	// if we would be losing an old command that hasn't been acknowledged,
477 	// we must drop the connection
478 	// also leave one slot open for the disconnect command in this case.
479 
480 	if ((isDisconnectCmd && unacknowledged > MAX_RELIABLE_COMMANDS) ||
481 	    (!isDisconnectCmd && unacknowledged >= MAX_RELIABLE_COMMANDS))
482 	{
483 		if(com_errorEntered)
484 			return;
485 		else
486 			Com_Error(ERR_DROP, "Client command overflow");
487 	}
488 
489 	Q_strncpyz(clc.reliableCommands[++clc.reliableSequence & (MAX_RELIABLE_COMMANDS - 1)],
490 		   cmd, sizeof(*clc.reliableCommands));
491 }
492 
493 /*
494 ======================
495 CL_ChangeReliableCommand
496 ======================
497 */
CL_ChangeReliableCommand(void)498 void CL_ChangeReliableCommand( void ) {
499 	int r, index, l;
500 
501 	r = clc.reliableSequence - (random() * 5);
502 	index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
503 	l = strlen(clc.reliableCommands[ index ]);
504 	if ( l >= MAX_STRING_CHARS - 1 ) {
505 		l = MAX_STRING_CHARS - 2;
506 	}
507 	clc.reliableCommands[ index ][ l ] = '\n';
508 	clc.reliableCommands[ index ][ l+1 ] = '\0';
509 }
510 
511 /*
512 =======================================================================
513 
514 CLIENT SIDE DEMO RECORDING
515 
516 =======================================================================
517 */
518 
519 /*
520 ====================
521 CL_WriteDemoMessage
522 
523 Dumps the current net message, prefixed by the length
524 ====================
525 */
526 
CL_WriteDemoMessage(msg_t * msg,int headerBytes)527 void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) {
528 	int		len, swlen;
529 
530 	// write the packet sequence
531 	len = clc.serverMessageSequence;
532 	swlen = LittleLong( len );
533 	FS_Write (&swlen, 4, clc.demofile);
534 
535 	// skip the packet sequencing information
536 	len = msg->cursize - headerBytes;
537 	swlen = LittleLong(len);
538 	FS_Write (&swlen, 4, clc.demofile);
539 	FS_Write ( msg->data + headerBytes, len, clc.demofile );
540 }
541 
542 
543 /*
544 ====================
545 CL_StopRecording_f
546 
547 stop recording a demo
548 ====================
549 */
CL_StopRecord_f(void)550 void CL_StopRecord_f( void ) {
551 	int		len;
552 
553 	if ( !clc.demorecording ) {
554 		Com_Printf ("Not recording a demo.\n");
555 		return;
556 	}
557 
558 	// finish up
559 	len = -1;
560 	FS_Write (&len, 4, clc.demofile);
561 	FS_Write (&len, 4, clc.demofile);
562 	FS_FCloseFile (clc.demofile);
563 	clc.demofile = 0;
564 	clc.demorecording = qfalse;
565 	clc.spDemoRecording = qfalse;
566 	Com_Printf ("Stopped demo.\n");
567 }
568 
569 /*
570 ==================
571 CL_DemoFilename
572 ==================
573 */
CL_DemoFilename(int number,char * fileName)574 void CL_DemoFilename( int number, char *fileName ) {
575 	int		a,b,c,d;
576 
577 	if(number < 0 || number > 9999)
578 		number = 9999;
579 
580 	a = number / 1000;
581 	number -= a*1000;
582 	b = number / 100;
583 	number -= b*100;
584 	c = number / 10;
585 	number -= c*10;
586 	d = number;
587 
588 	Com_sprintf( fileName, MAX_OSPATH, "demo%i%i%i%i"
589 		, a, b, c, d );
590 }
591 
592 /*
593 ====================
594 CL_Record_f
595 
596 record <demoname>
597 
598 Begins recording a demo from the current position
599 ====================
600 */
601 static char		demoName[MAX_QPATH];	// compiler bug workaround
CL_Record_f(void)602 void CL_Record_f( void ) {
603 	char		name[MAX_OSPATH];
604 	byte		bufData[MAX_MSGLEN];
605 	msg_t	buf;
606 	int			i;
607 	int			len;
608 	entityState_t	*ent;
609 	entityState_t	nullstate;
610 	char		*s;
611 
612 	if ( Cmd_Argc() > 2 ) {
613 		Com_Printf ("record <demoname>\n");
614 		return;
615 	}
616 
617 	if ( clc.demorecording ) {
618 		if (!clc.spDemoRecording) {
619 			Com_Printf ("Already recording.\n");
620 		}
621 		return;
622 	}
623 
624 	if ( cls.state != CA_ACTIVE ) {
625 		Com_Printf ("You must be in a level to record.\n");
626 		return;
627 	}
628 
629   // sync 0 doesn't prevent recording, so not forcing it off .. everyone does g_sync 1 ; record ; g_sync 0 ..
630 	if ( NET_IsLocalAddress( clc.serverAddress ) && !Cvar_VariableValue( "g_synchronousClients" ) ) {
631 		Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n");
632 	}
633 
634 	if ( Cmd_Argc() == 2 ) {
635 		s = Cmd_Argv(1);
636 		Q_strncpyz( demoName, s, sizeof( demoName ) );
637 		Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION );
638 	} else {
639 		int		number;
640 
641 		// scan for a free demo name
642 		for ( number = 0 ; number <= 9999 ; number++ ) {
643 			CL_DemoFilename( number, demoName );
644 			Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION );
645 
646 			if (!FS_FileExists(name))
647 				break;	// file doesn't exist
648 		}
649 	}
650 
651 	// open the demo file
652 
653 	Com_Printf ("recording to %s.\n", name);
654 	clc.demofile = FS_FOpenFileWrite( name );
655 	if ( !clc.demofile ) {
656 		Com_Printf ("ERROR: couldn't open.\n");
657 		return;
658 	}
659 	clc.demorecording = qtrue;
660 	if (Cvar_VariableValue("ui_recordSPDemo")) {
661 	  clc.spDemoRecording = qtrue;
662 	} else {
663 	  clc.spDemoRecording = qfalse;
664 	}
665 
666 
667 	Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) );
668 
669 	// don't start saving messages until a non-delta compressed message is received
670 	clc.demowaiting = qtrue;
671 
672 	// write out the gamestate message
673 	MSG_Init (&buf, bufData, sizeof(bufData));
674 	MSG_Bitstream(&buf);
675 
676 	// NOTE, MRE: all server->client messages now acknowledge
677 	MSG_WriteLong( &buf, clc.reliableSequence );
678 
679 	MSG_WriteByte (&buf, svc_gamestate);
680 	MSG_WriteLong (&buf, clc.serverCommandSequence );
681 
682 	// configstrings
683 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
684 		if ( !cl.gameState.stringOffsets[i] ) {
685 			continue;
686 		}
687 		s = cl.gameState.stringData + cl.gameState.stringOffsets[i];
688 		MSG_WriteByte (&buf, svc_configstring);
689 		MSG_WriteShort (&buf, i);
690 		MSG_WriteBigString (&buf, s);
691 	}
692 
693 	// baselines
694 	Com_Memset (&nullstate, 0, sizeof(nullstate));
695 	for ( i = 0; i < MAX_GENTITIES ; i++ ) {
696 		ent = &cl.entityBaselines[i];
697 		if ( !ent->number ) {
698 			continue;
699 		}
700 		MSG_WriteByte (&buf, svc_baseline);
701 		MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue );
702 	}
703 
704 	MSG_WriteByte( &buf, svc_EOF );
705 
706 	// finished writing the gamestate stuff
707 
708 	// write the client num
709 	MSG_WriteLong(&buf, clc.clientNum);
710 	// write the checksum feed
711 	MSG_WriteLong(&buf, clc.checksumFeed);
712 
713 	// finished writing the client packet
714 	MSG_WriteByte( &buf, svc_EOF );
715 
716 	// write it to the demo file
717 	len = LittleLong( clc.serverMessageSequence - 1 );
718 	FS_Write (&len, 4, clc.demofile);
719 
720 	len = LittleLong (buf.cursize);
721 	FS_Write (&len, 4, clc.demofile);
722 	FS_Write (buf.data, buf.cursize, clc.demofile);
723 
724 	// the rest of the demo file will be copied from net messages
725 }
726 
727 /*
728 =======================================================================
729 
730 CLIENT SIDE DEMO PLAYBACK
731 
732 =======================================================================
733 */
734 
735 /*
736 =================
737 CL_DemoFrameDurationSDev
738 =================
739 */
CL_DemoFrameDurationSDev(void)740 static float CL_DemoFrameDurationSDev( void )
741 {
742 	int i;
743 	int numFrames;
744 	float mean = 0.0f;
745 	float variance = 0.0f;
746 
747 	if( ( clc.timeDemoFrames - 1 ) > MAX_TIMEDEMO_DURATIONS )
748 		numFrames = MAX_TIMEDEMO_DURATIONS;
749 	else
750 		numFrames = clc.timeDemoFrames - 1;
751 
752 	for( i = 0; i < numFrames; i++ )
753 		mean += clc.timeDemoDurations[ i ];
754 	mean /= numFrames;
755 
756 	for( i = 0; i < numFrames; i++ )
757 	{
758 		float x = clc.timeDemoDurations[ i ];
759 
760 		variance += ( ( x - mean ) * ( x - mean ) );
761 	}
762 	variance /= numFrames;
763 
764 	return sqrt( variance );
765 }
766 
767 /*
768 =================
769 CL_DemoCompleted
770 =================
771 */
CL_DemoCompleted(void)772 void CL_DemoCompleted( void )
773 {
774 	char buffer[ MAX_STRING_CHARS ];
775 
776 	if( cl_timedemo && cl_timedemo->integer )
777 	{
778 		int	time;
779 
780 		time = Sys_Milliseconds() - clc.timeDemoStart;
781 		if( time > 0 )
782 		{
783 			// Millisecond times are frame durations:
784 			// minimum/average/maximum/std deviation
785 			Com_sprintf( buffer, sizeof( buffer ),
786 					"%i frames %3.1f seconds %3.1f fps %d.0/%.1f/%d.0/%.1f ms\n",
787 					clc.timeDemoFrames,
788 					time/1000.0,
789 					clc.timeDemoFrames*1000.0 / time,
790 					clc.timeDemoMinDuration,
791 					time / (float)clc.timeDemoFrames,
792 					clc.timeDemoMaxDuration,
793 					CL_DemoFrameDurationSDev( ) );
794 			Com_Printf( "%s", buffer );
795 
796 			// Write a log of all the frame durations
797 			if( cl_timedemoLog && strlen( cl_timedemoLog->string ) > 0 )
798 			{
799 				int i;
800 				int numFrames;
801 				fileHandle_t f;
802 
803 				if( ( clc.timeDemoFrames - 1 ) > MAX_TIMEDEMO_DURATIONS )
804 					numFrames = MAX_TIMEDEMO_DURATIONS;
805 				else
806 					numFrames = clc.timeDemoFrames - 1;
807 
808 				f = FS_FOpenFileWrite( cl_timedemoLog->string );
809 				if( f )
810 				{
811 					FS_Printf( f, "# %s", buffer );
812 
813 					for( i = 0; i < numFrames; i++ )
814 						FS_Printf( f, "%d\n", clc.timeDemoDurations[ i ] );
815 
816 					FS_FCloseFile( f );
817 					Com_Printf( "%s written\n", cl_timedemoLog->string );
818 				}
819 				else
820 				{
821 					Com_Printf( "Couldn't open %s for writing\n",
822 							cl_timedemoLog->string );
823 				}
824 			}
825 		}
826 	}
827 
828 	CL_Disconnect( qtrue );
829 	CL_NextDemo();
830 }
831 
832 /*
833 =================
834 CL_ReadDemoMessage
835 =================
836 */
CL_ReadDemoMessage(void)837 void CL_ReadDemoMessage( void ) {
838 	int			r;
839 	msg_t		buf;
840 	byte		bufData[ MAX_MSGLEN ];
841 	int			s;
842 
843 	if ( !clc.demofile ) {
844 		CL_DemoCompleted ();
845 		return;
846 	}
847 
848 	// get the sequence number
849 	r = FS_Read( &s, 4, clc.demofile);
850 	if ( r != 4 ) {
851 		CL_DemoCompleted ();
852 		return;
853 	}
854 	clc.serverMessageSequence = LittleLong( s );
855 
856 	// init the message
857 	MSG_Init( &buf, bufData, sizeof( bufData ) );
858 
859 	// get the length
860 	r = FS_Read (&buf.cursize, 4, clc.demofile);
861 	if ( r != 4 ) {
862 		CL_DemoCompleted ();
863 		return;
864 	}
865 	buf.cursize = LittleLong( buf.cursize );
866 	if ( buf.cursize == -1 ) {
867 		CL_DemoCompleted ();
868 		return;
869 	}
870 	if ( buf.cursize > buf.maxsize ) {
871 		Com_Error (ERR_DROP, "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN");
872 	}
873 	r = FS_Read( buf.data, buf.cursize, clc.demofile );
874 	if ( r != buf.cursize ) {
875 		Com_Printf( "Demo file was truncated.\n");
876 		CL_DemoCompleted ();
877 		return;
878 	}
879 
880 	clc.lastPacketTime = cls.realtime;
881 	buf.readcount = 0;
882 	CL_ParseServerMessage( &buf );
883 }
884 
885 /*
886 ====================
887 CL_WalkDemoExt
888 ====================
889 */
CL_WalkDemoExt(char * arg,char * name,int * demofile)890 static void CL_WalkDemoExt(char *arg, char *name, int *demofile)
891 {
892 	int i = 0;
893 	*demofile = 0;
894 	while(demo_protocols[i])
895 	{
896 		Com_sprintf (name, MAX_OSPATH, "demos/%s.dm_%d", arg, demo_protocols[i]);
897 		FS_FOpenFileRead( name, demofile, qtrue );
898 		if (*demofile)
899 		{
900 			Com_Printf("Demo file: %s\n", name);
901 			break;
902 		}
903 		else
904 			Com_Printf("Not found: %s\n", name);
905 		i++;
906 	}
907 }
908 
909 /*
910 ====================
911 CL_CompleteDemoName
912 ====================
913 */
CL_CompleteDemoName(char * args,int argNum)914 static void CL_CompleteDemoName( char *args, int argNum )
915 {
916 	if( argNum == 2 )
917 	{
918 		char demoExt[ 16 ];
919 
920 		Com_sprintf( demoExt, sizeof( demoExt ), ".dm_%d", PROTOCOL_VERSION );
921 		Field_CompleteFilename( "demos", demoExt, qtrue, qtrue );
922 	}
923 }
924 
925 /*
926 ====================
927 CL_PlayDemo_f
928 
929 demo <demoname>
930 
931 ====================
932 */
CL_PlayDemo_f(void)933 void CL_PlayDemo_f( void ) {
934 	char		name[MAX_OSPATH];
935 	char		*arg, *ext_test;
936 	int			protocol, i;
937 	char		retry[MAX_OSPATH];
938 
939 	if (Cmd_Argc() != 2) {
940 		Com_Printf ("demo <demoname>\n");
941 		return;
942 	}
943 
944 	// make sure a local server is killed
945 	// 2 means don't force disconnect of local client
946 	Cvar_Set( "sv_killserver", "2" );
947 
948 	// open the demo file
949 	arg = Cmd_Argv(1);
950 
951 	CL_Disconnect( qtrue );
952 
953 	// check for an extension .dm_?? (?? is protocol)
954 	ext_test = arg + strlen(arg) - 6;
955 	if ((strlen(arg) > 6) && (ext_test[0] == '.') &&
956 		((ext_test[1] == 'd') || (ext_test[1] == 'D')) &&
957 		((ext_test[2] == 'm') || (ext_test[2] == 'M')) &&
958 		(ext_test[3] == '_'))
959 	{
960 		protocol = atoi(ext_test+4);
961 		i=0;
962 		while(demo_protocols[i])
963 		{
964 			if (demo_protocols[i] == protocol)
965 				break;
966 			i++;
967 		}
968 		if (demo_protocols[i])
969 		{
970 			Com_sprintf (name, sizeof(name), "demos/%s", arg);
971 			FS_FOpenFileRead( name, &clc.demofile, qtrue );
972 		} else {
973 			Com_Printf("Protocol %d not supported for demos\n", protocol);
974 			Q_strncpyz(retry, arg, sizeof(retry));
975 			retry[strlen(retry)-6] = 0;
976 			CL_WalkDemoExt( retry, name, &clc.demofile );
977 		}
978 	} else {
979 		CL_WalkDemoExt( arg, name, &clc.demofile );
980 	}
981 
982 	if (!clc.demofile) {
983 		Com_Error( ERR_DROP, "couldn't open %s", name);
984 		return;
985 	}
986 	Q_strncpyz( clc.demoName, Cmd_Argv(1), sizeof( clc.demoName ) );
987 
988 	Con_Close();
989 
990 	cls.state = CA_CONNECTED;
991 	clc.demoplaying = qtrue;
992 	Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) );
993 
994 	// read demo messages until connected
995 	while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) {
996 		CL_ReadDemoMessage();
997 	}
998 	// don't get the first snapshot this frame, to prevent the long
999 	// time from the gamestate load from messing causing a time skip
1000 	clc.firstDemoFrameSkipped = qfalse;
1001 }
1002 
1003 
1004 /*
1005 ====================
1006 CL_StartDemoLoop
1007 
1008 Closing the main menu will restart the demo loop
1009 ====================
1010 */
CL_StartDemoLoop(void)1011 void CL_StartDemoLoop( void ) {
1012 	// start the demo loop again
1013 	Cbuf_AddText ("d1\n");
1014 	Key_SetCatcher( 0 );
1015 }
1016 
1017 /*
1018 ==================
1019 CL_NextDemo
1020 
1021 Called when a demo or cinematic finishes
1022 If the "nextdemo" cvar is set, that command will be issued
1023 ==================
1024 */
CL_NextDemo(void)1025 void CL_NextDemo( void ) {
1026 	char	v[MAX_STRING_CHARS];
1027 
1028 	Q_strncpyz( v, Cvar_VariableString ("nextdemo"), sizeof(v) );
1029 	v[MAX_STRING_CHARS-1] = 0;
1030 	Com_DPrintf("CL_NextDemo: %s\n", v );
1031 	if (!v[0]) {
1032 		return;
1033 	}
1034 
1035 	Cvar_Set ("nextdemo","");
1036 	Cbuf_AddText (v);
1037 	Cbuf_AddText ("\n");
1038 	Cbuf_Execute();
1039 }
1040 
1041 
1042 //======================================================================
1043 
1044 /*
1045 =====================
1046 CL_ShutdownAll
1047 =====================
1048 */
CL_ShutdownAll(void)1049 void CL_ShutdownAll(void) {
1050 
1051 #ifdef USE_CURL
1052 	CL_cURL_Shutdown();
1053 #endif
1054 	// clear sounds
1055 	S_DisableSounds();
1056 	// shutdown CGame
1057 	CL_ShutdownCGame();
1058 	// shutdown UI
1059 	CL_ShutdownUI();
1060 
1061 	// shutdown the renderer
1062 	if ( re.Shutdown ) {
1063 		re.Shutdown( qfalse );		// don't destroy window or context
1064 	}
1065 
1066 	cls.uiStarted = qfalse;
1067 	cls.cgameStarted = qfalse;
1068 	cls.rendererStarted = qfalse;
1069 	cls.soundRegistered = qfalse;
1070 }
1071 
1072 /*
1073 =================
1074 CL_FlushMemory
1075 
1076 Called by CL_MapLoading, CL_Connect_f, CL_PlayDemo_f, and CL_ParseGamestate the only
1077 ways a client gets into a game
1078 Also called by Com_Error
1079 =================
1080 */
CL_FlushMemory(void)1081 void CL_FlushMemory( void ) {
1082 
1083 	// shutdown all the client stuff
1084 	CL_ShutdownAll();
1085 
1086 	// if not running a server clear the whole hunk
1087 	if ( !com_sv_running->integer ) {
1088 		// clear the whole hunk
1089 		Hunk_Clear();
1090 		// clear collision map data
1091 		CM_ClearMap();
1092 	}
1093 	else {
1094 		// clear all the client data on the hunk
1095 		Hunk_ClearToMark();
1096 	}
1097 
1098 	CL_StartHunkUsers( qfalse );
1099 }
1100 
1101 /*
1102 =====================
1103 CL_MapLoading
1104 
1105 A local server is starting to load a map, so update the
1106 screen to let the user know about it, then dump all client
1107 memory on the hunk from cgame, ui, and renderer
1108 =====================
1109 */
CL_MapLoading(void)1110 void CL_MapLoading( void ) {
1111 	if ( com_dedicated->integer ) {
1112 		cls.state = CA_DISCONNECTED;
1113 		Key_SetCatcher( KEYCATCH_CONSOLE );
1114 		return;
1115 	}
1116 
1117 	if ( !com_cl_running->integer ) {
1118 		return;
1119 	}
1120 
1121 	Con_Close();
1122 	Key_SetCatcher( 0 );
1123 
1124 	// if we are already connected to the local host, stay connected
1125 	if ( cls.state >= CA_CONNECTED && !Q_stricmp( cls.servername, "localhost" ) ) {
1126 		cls.state = CA_CONNECTED;		// so the connect screen is drawn
1127 		Com_Memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) );
1128 		Com_Memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) );
1129 		Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) );
1130 		clc.lastPacketSentTime = -9999;
1131 		SCR_UpdateScreen();
1132 	} else {
1133 		// clear nextmap so the cinematic shutdown doesn't execute it
1134 		Cvar_Set( "nextmap", "" );
1135 		CL_Disconnect( qtrue );
1136 		Q_strncpyz( cls.servername, "localhost", sizeof(cls.servername) );
1137 		cls.state = CA_CHALLENGING;		// so the connect screen is drawn
1138 		Key_SetCatcher( 0 );
1139 		SCR_UpdateScreen();
1140 		clc.connectTime = -RETRANSMIT_TIMEOUT;
1141 		NET_StringToAdr( cls.servername, &clc.serverAddress, NA_UNSPEC);
1142 		// we don't need a challenge on the localhost
1143 
1144 		CL_CheckForResend();
1145 	}
1146 }
1147 
1148 /*
1149 =====================
1150 CL_ClearState
1151 
1152 Called before parsing a gamestate
1153 =====================
1154 */
CL_ClearState(void)1155 void CL_ClearState (void) {
1156 
1157 //	S_StopAllSounds();
1158 
1159 	Com_Memset( &cl, 0, sizeof( cl ) );
1160 }
1161 
1162 /*
1163 ====================
1164 CL_UpdateGUID
1165 
1166 update cl_guid using QKEY_FILE and optional prefix
1167 ====================
1168 */
CL_UpdateGUID(const char * prefix,int prefix_len)1169 static void CL_UpdateGUID( const char *prefix, int prefix_len )
1170 {
1171 	fileHandle_t f;
1172 	int len;
1173 
1174 	len = FS_SV_FOpenFileRead( QKEY_FILE, &f );
1175 	FS_FCloseFile( f );
1176 
1177 	if( len != QKEY_SIZE )
1178 		Cvar_Set( "cl_guid", "" );
1179 	else
1180 		Cvar_Set( "cl_guid", Com_MD5File( QKEY_FILE, QKEY_SIZE,
1181 			prefix, prefix_len ) );
1182 }
1183 
1184 
1185 /*
1186 =====================
1187 CL_Disconnect
1188 
1189 Called when a connection, demo, or cinematic is being terminated.
1190 Goes from a connected state to either a menu state or a console state
1191 Sends a disconnect message to the server
1192 This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors
1193 =====================
1194 */
CL_Disconnect(qboolean showMainMenu)1195 void CL_Disconnect( qboolean showMainMenu ) {
1196 	if ( !com_cl_running || !com_cl_running->integer ) {
1197 		return;
1198 	}
1199 
1200 	// shutting down the client so enter full screen ui mode
1201 	Cvar_Set("r_uiFullScreen", "1");
1202 
1203 	if ( clc.demorecording ) {
1204 		CL_StopRecord_f ();
1205 	}
1206 
1207 	if (clc.download) {
1208 		FS_FCloseFile( clc.download );
1209 		clc.download = 0;
1210 	}
1211 	*clc.downloadTempName = *clc.downloadName = 0;
1212 	Cvar_Set( "cl_downloadName", "" );
1213 
1214 #ifdef USE_MUMBLE
1215 	if (cl_useMumble->integer && mumble_islinked()) {
1216 		Com_Printf("Mumble: Unlinking from Mumble application\n");
1217 		mumble_unlink();
1218 	}
1219 #endif
1220 
1221 #ifdef USE_VOIP
1222 	if (cl_voipSend->integer) {
1223 		int tmp = cl_voipUseVAD->integer;
1224 		cl_voipUseVAD->integer = 0;  // disable this for a moment.
1225 		clc.voipOutgoingDataSize = 0;  // dump any pending VoIP transmission.
1226 		Cvar_Set("cl_voipSend", "0");
1227 		CL_CaptureVoip();  // clean up any state...
1228 		cl_voipUseVAD->integer = tmp;
1229 	}
1230 
1231 	if (clc.speexInitialized) {
1232 		int i;
1233 		speex_bits_destroy(&clc.speexEncoderBits);
1234 		speex_encoder_destroy(clc.speexEncoder);
1235 		speex_preprocess_state_destroy(clc.speexPreprocessor);
1236 		for (i = 0; i < MAX_CLIENTS; i++) {
1237 			speex_bits_destroy(&clc.speexDecoderBits[i]);
1238 			speex_decoder_destroy(clc.speexDecoder[i]);
1239 		}
1240 	}
1241 	Cmd_RemoveCommand ("voip");
1242 #endif
1243 
1244 	if ( clc.demofile ) {
1245 		FS_FCloseFile( clc.demofile );
1246 		clc.demofile = 0;
1247 	}
1248 
1249 	if ( uivm && showMainMenu ) {
1250 		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE );
1251 	}
1252 
1253 	SCR_StopCinematic ();
1254 	S_ClearSoundBuffer();
1255 
1256 	// send a disconnect message to the server
1257 	// send it a few times in case one is dropped
1258 	if ( cls.state >= CA_CONNECTED ) {
1259 		CL_AddReliableCommand("disconnect", qtrue);
1260 		CL_WritePacket();
1261 		CL_WritePacket();
1262 		CL_WritePacket();
1263 	}
1264 
1265 	// Remove pure paks
1266 	FS_PureServerSetLoadedPaks("", "");
1267 
1268 	CL_ClearState ();
1269 
1270 	// wipe the client connection
1271 	Com_Memset( &clc, 0, sizeof( clc ) );
1272 
1273 	cls.state = CA_DISCONNECTED;
1274 
1275 	// allow cheats locally
1276 	Cvar_Set( "sv_cheats", "1" );
1277 
1278 	// not connected to a pure server anymore
1279 	cl_connectedToPureServer = qfalse;
1280 
1281 #ifdef USE_VOIP
1282 	// not connected to voip server anymore.
1283 	cl_connectedToVoipServer = qfalse;
1284 #endif
1285 
1286 	// Stop recording any video
1287 	if( CL_VideoRecording( ) ) {
1288 		// Finish rendering current frame
1289 		SCR_UpdateScreen( );
1290 		CL_CloseAVI( );
1291 	}
1292 	CL_UpdateGUID( NULL, 0 );
1293 }
1294 
1295 
1296 /*
1297 ===================
1298 CL_ForwardCommandToServer
1299 
1300 adds the current command line as a clientCommand
1301 things like godmode, noclip, etc, are commands directed to the server,
1302 so when they are typed in at the console, they will need to be forwarded.
1303 ===================
1304 */
CL_ForwardCommandToServer(const char * string)1305 void CL_ForwardCommandToServer( const char *string ) {
1306 	char	*cmd;
1307 
1308 	cmd = Cmd_Argv(0);
1309 
1310 	// ignore key up commands
1311 	if ( cmd[0] == '-' ) {
1312 		return;
1313 	}
1314 
1315 	if ( clc.demoplaying || cls.state < CA_CONNECTED || cmd[0] == '+' ) {
1316 		Com_Printf ("Unknown command \"%s" S_COLOR_WHITE "\"\n", cmd);
1317 		return;
1318 	}
1319 
1320 	if ( Cmd_Argc() > 1 ) {
1321 		CL_AddReliableCommand(string, qfalse);
1322 	} else {
1323 		CL_AddReliableCommand(cmd, qfalse);
1324 	}
1325 }
1326 
1327 /*
1328 ===================
1329 CL_RequestMotd
1330 
1331 ===================
1332 */
CL_RequestMotd(void)1333 void CL_RequestMotd( void ) {
1334 	char		info[MAX_INFO_STRING];
1335 
1336 	if ( !cl_motd->integer ) {
1337 		return;
1338 	}
1339 	Com_Printf( "Resolving %s\n", UPDATE_SERVER_NAME );
1340 	if ( !NET_StringToAdr( UPDATE_SERVER_NAME, &cls.updateServer, NA_IP ) ) {
1341 		Com_Printf( "Couldn't resolve address\n" );
1342 		return;
1343 	}
1344 	cls.updateServer.port = BigShort( PORT_UPDATE );
1345 	Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME,
1346 		cls.updateServer.ip[0], cls.updateServer.ip[1],
1347 		cls.updateServer.ip[2], cls.updateServer.ip[3],
1348 		BigShort( cls.updateServer.port ) );
1349 
1350 	info[0] = 0;
1351 
1352 	Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds());
1353 
1354 	Info_SetValueForKey( info, "challenge", cls.updateChallenge );
1355 	Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string );
1356 	Info_SetValueForKey( info, "version", com_version->string );
1357 
1358 	NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info );
1359 }
1360 
1361 /*
1362 ===================
1363 CL_RequestAuthorization
1364 
1365 Authorization server protocol
1366 -----------------------------
1367 
1368 All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff).
1369 
1370 Whenever the client tries to get a challenge from the server it wants to
1371 connect to, it also blindly fires off a packet to the authorize server:
1372 
1373 getKeyAuthorize <challenge> <cdkey>
1374 
1375 cdkey may be "demo"
1376 
1377 
1378 #OLD The authorize server returns a:
1379 #OLD
1380 #OLD keyAthorize <challenge> <accept | deny>
1381 #OLD
1382 #OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP
1383 #OLD address in the last 15 minutes.
1384 
1385 
1386 The server sends a:
1387 
1388 getIpAuthorize <challenge> <ip>
1389 
1390 The authorize server returns a:
1391 
1392 ipAuthorize <challenge> <accept | deny | demo | unknown >
1393 
1394 A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes.
1395 If no response is received from the authorize server after two tries, the client will be let
1396 in anyway.
1397 ===================
1398 */
1399 #ifndef STANDALONE
CL_RequestAuthorization(void)1400 void CL_RequestAuthorization( void ) {
1401 	char	nums[64];
1402 	int		i, j, l;
1403 	cvar_t	*fs;
1404 
1405 	if ( !cls.authorizeServer.port ) {
1406 		Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME );
1407 		if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer, NA_IP ) ) {
1408 			Com_Printf( "Couldn't resolve address\n" );
1409 			return;
1410 		}
1411 
1412 		cls.authorizeServer.port = BigShort( PORT_AUTHORIZE );
1413 		Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
1414 			cls.authorizeServer.ip[0], cls.authorizeServer.ip[1],
1415 			cls.authorizeServer.ip[2], cls.authorizeServer.ip[3],
1416 			BigShort( cls.authorizeServer.port ) );
1417 	}
1418 	if ( cls.authorizeServer.type == NA_BAD ) {
1419 		return;
1420 	}
1421 
1422 	// only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces
1423 	j = 0;
1424 	l = strlen( cl_cdkey );
1425 	if ( l > 32 ) {
1426 		l = 32;
1427 	}
1428 	for ( i = 0 ; i < l ; i++ ) {
1429 		if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' )
1430 				|| ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' )
1431 				|| ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' )
1432 			 ) {
1433 			nums[j] = cl_cdkey[i];
1434 			j++;
1435 		}
1436 	}
1437 	nums[j] = 0;
1438 
1439 	fs = Cvar_Get ("cl_anonymous", "0", CVAR_INIT|CVAR_SYSTEMINFO );
1440 
1441 	NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, "getKeyAuthorize %i %s", fs->integer, nums );
1442 }
1443 #endif
1444 /*
1445 ======================================================================
1446 
1447 CONSOLE COMMANDS
1448 
1449 ======================================================================
1450 */
1451 
1452 /*
1453 ==================
1454 CL_ForwardToServer_f
1455 ==================
1456 */
CL_ForwardToServer_f(void)1457 void CL_ForwardToServer_f( void ) {
1458 	if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
1459 		Com_Printf ("Not connected to a server.\n");
1460 		return;
1461 	}
1462 
1463 	// don't forward the first argument
1464 	if ( Cmd_Argc() > 1 ) {
1465 		CL_AddReliableCommand(Cmd_Args(), qfalse);
1466 	}
1467 }
1468 
1469 /*
1470 ==================
1471 CL_Disconnect_f
1472 ==================
1473 */
CL_Disconnect_f(void)1474 void CL_Disconnect_f( void ) {
1475 	SCR_StopCinematic();
1476 	Cvar_Set("ui_singlePlayerActive", "0");
1477 	if ( cls.state != CA_DISCONNECTED && cls.state != CA_CINEMATIC ) {
1478 		Com_Error (ERR_DISCONNECT, "Disconnected from server");
1479 	}
1480 }
1481 
1482 
1483 /*
1484 ================
1485 CL_Reconnect_f
1486 
1487 ================
1488 */
CL_Reconnect_f(void)1489 void CL_Reconnect_f( void ) {
1490 	if ( !strlen( cls.servername ) || !strcmp( cls.servername, "localhost" ) ) {
1491 		Com_Printf( "Can't reconnect to localhost.\n" );
1492 		return;
1493 	}
1494 	Cvar_Set("ui_singlePlayerActive", "0");
1495 	Cbuf_AddText( va("connect %s\n", cls.servername ) );
1496 }
1497 
1498 /*
1499 ================
1500 CL_Connect_f
1501 
1502 ================
1503 */
CL_Connect_f(void)1504 void CL_Connect_f( void ) {
1505 	char	*server;
1506 	const char	*serverString;
1507 	int argc = Cmd_Argc();
1508 	netadrtype_t family = NA_UNSPEC;
1509 
1510 	if ( argc != 2 && argc != 3 ) {
1511 		Com_Printf( "usage: connect [-4|-6] server\n");
1512 		return;
1513 	}
1514 
1515 	if(argc == 2)
1516 		server = Cmd_Argv(1);
1517 	else
1518 	{
1519 		if(!strcmp(Cmd_Argv(1), "-4"))
1520 			family = NA_IP;
1521 		else if(!strcmp(Cmd_Argv(1), "-6"))
1522 			family = NA_IP6;
1523 		else
1524 			Com_Printf( "warning: only -4 or -6 as address type understood.\n");
1525 
1526 		server = Cmd_Argv(2);
1527 	}
1528 
1529 	Cvar_Set("ui_singlePlayerActive", "0");
1530 
1531 	// fire a message off to the motd server
1532 	CL_RequestMotd();
1533 
1534 	// clear any previous "server full" type messages
1535 	clc.serverMessage[0] = 0;
1536 
1537 	if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) {
1538 		// if running a local server, kill it
1539 		SV_Shutdown( "Server quit" );
1540 	}
1541 
1542 	// make sure a local server is killed
1543 	Cvar_Set( "sv_killserver", "1" );
1544 	SV_Frame( 0 );
1545 
1546 	CL_Disconnect( qtrue );
1547 	Con_Close();
1548 
1549 	Q_strncpyz( cls.servername, server, sizeof(cls.servername) );
1550 
1551 	if (!NET_StringToAdr(cls.servername, &clc.serverAddress, family) ) {
1552 		Com_Printf ("Bad server address\n");
1553 		cls.state = CA_DISCONNECTED;
1554 		return;
1555 	}
1556 	if (clc.serverAddress.port == 0) {
1557 		clc.serverAddress.port = BigShort( PORT_SERVER );
1558 	}
1559 
1560 	serverString = NET_AdrToStringwPort(clc.serverAddress);
1561 
1562 	Com_Printf( "%s resolved to %s\n", cls.servername, serverString);
1563 
1564 	if( cl_guidServerUniq->integer )
1565 		CL_UpdateGUID( serverString, strlen( serverString ) );
1566 	else
1567 		CL_UpdateGUID( NULL, 0 );
1568 
1569 	// if we aren't playing on a lan, we need to authenticate
1570 	// with the cd key
1571 	if(NET_IsLocalAddress(clc.serverAddress))
1572 		cls.state = CA_CHALLENGING;
1573 	else
1574 	{
1575 		cls.state = CA_CONNECTING;
1576 
1577 		// Set a client challenge number that ideally is mirrored back by the server.
1578 		clc.challenge = ((rand() << 16) ^ rand()) ^ Com_Milliseconds();
1579 	}
1580 
1581 	Key_SetCatcher( 0 );
1582 	clc.connectTime = -99999;	// CL_CheckForResend() will fire immediately
1583 	clc.connectPacketCount = 0;
1584 
1585 	// server connection string
1586 	Cvar_Set( "cl_currentServerAddress", server );
1587 }
1588 
1589 #define MAX_RCON_MESSAGE 1024
1590 
1591 /*
1592 ==================
1593 CL_CompleteRcon
1594 ==================
1595 */
CL_CompleteRcon(char * args,int argNum)1596 static void CL_CompleteRcon( char *args, int argNum )
1597 {
1598 	if( argNum == 2 )
1599 	{
1600 		// Skip "rcon "
1601 		char *p = Com_SkipTokens( args, 1, " " );
1602 
1603 		if( p > args )
1604 			Field_CompleteCommand( p, qtrue, qtrue );
1605 	}
1606 }
1607 
1608 /*
1609 =====================
1610 CL_Rcon_f
1611 
1612   Send the rest of the command line over as
1613   an unconnected command.
1614 =====================
1615 */
CL_Rcon_f(void)1616 void CL_Rcon_f( void ) {
1617 	char	message[MAX_RCON_MESSAGE];
1618 	netadr_t	to;
1619 
1620 	if ( !rcon_client_password->string ) {
1621 		Com_Printf ("You must set 'rconpassword' before\n"
1622 					"issuing an rcon command.\n");
1623 		return;
1624 	}
1625 
1626 	message[0] = -1;
1627 	message[1] = -1;
1628 	message[2] = -1;
1629 	message[3] = -1;
1630 	message[4] = 0;
1631 
1632 	Q_strcat (message, MAX_RCON_MESSAGE, "rcon ");
1633 
1634 	Q_strcat (message, MAX_RCON_MESSAGE, rcon_client_password->string);
1635 	Q_strcat (message, MAX_RCON_MESSAGE, " ");
1636 
1637 	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
1638 	Q_strcat (message, MAX_RCON_MESSAGE, Cmd_Cmd()+5);
1639 
1640 	if ( cls.state >= CA_CONNECTED ) {
1641 		to = clc.netchan.remoteAddress;
1642 	} else {
1643 		if (!strlen(rconAddress->string)) {
1644 			Com_Printf ("You must either be connected,\n"
1645 						"or set the 'rconAddress' cvar\n"
1646 						"to issue rcon commands\n");
1647 
1648 			return;
1649 		}
1650 		NET_StringToAdr (rconAddress->string, &to, NA_UNSPEC);
1651 		if (to.port == 0) {
1652 			to.port = BigShort (PORT_SERVER);
1653 		}
1654 	}
1655 
1656 	NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
1657 }
1658 
1659 /*
1660 =================
1661 CL_SendPureChecksums
1662 =================
1663 */
CL_SendPureChecksums(void)1664 void CL_SendPureChecksums( void ) {
1665 	char cMsg[MAX_INFO_VALUE];
1666 
1667 	// if we are pure we need to send back a command with our referenced pk3 checksums
1668 	Com_sprintf(cMsg, sizeof(cMsg), "cp %d %s", cl.serverId, FS_ReferencedPakPureChecksums());
1669 
1670 	CL_AddReliableCommand(cMsg, qfalse);
1671 }
1672 
1673 /*
1674 =================
1675 CL_ResetPureClientAtServer
1676 =================
1677 */
CL_ResetPureClientAtServer(void)1678 void CL_ResetPureClientAtServer( void ) {
1679 	CL_AddReliableCommand("vdr", qfalse);
1680 }
1681 
1682 /*
1683 =================
1684 CL_Vid_Restart_f
1685 
1686 Restart the video subsystem
1687 
1688 we also have to reload the UI and CGame because the renderer
1689 doesn't know what graphics to reload
1690 =================
1691 */
CL_Vid_Restart_f(void)1692 void CL_Vid_Restart_f( void ) {
1693 
1694 	// Settings may have changed so stop recording now
1695 	if( CL_VideoRecording( ) ) {
1696 		CL_CloseAVI( );
1697 	}
1698 
1699 	if(clc.demorecording)
1700 		CL_StopRecord_f();
1701 
1702 	// don't let them loop during the restart
1703 	S_StopAllSounds();
1704 	// shutdown the UI
1705 	CL_ShutdownUI();
1706 	// shutdown the CGame
1707 	CL_ShutdownCGame();
1708 	// shutdown the renderer and clear the renderer interface
1709 	CL_ShutdownRef();
1710 	// client is no longer pure untill new checksums are sent
1711 	CL_ResetPureClientAtServer();
1712 	// clear pak references
1713 	FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF );
1714 	// reinitialize the filesystem if the game directory or checksum has changed
1715 	FS_ConditionalRestart( clc.checksumFeed );
1716 
1717 	cls.rendererStarted = qfalse;
1718 	cls.uiStarted = qfalse;
1719 	cls.cgameStarted = qfalse;
1720 	cls.soundRegistered = qfalse;
1721 
1722 	// unpause so the cgame definately gets a snapshot and renders a frame
1723 	Cvar_Set( "cl_paused", "0" );
1724 
1725 	// if not running a server clear the whole hunk
1726 	if ( !com_sv_running->integer ) {
1727 		// clear the whole hunk
1728 		Hunk_Clear();
1729 	}
1730 	else {
1731 		// clear all the client data on the hunk
1732 		Hunk_ClearToMark();
1733 	}
1734 
1735 	// initialize the renderer interface
1736 	CL_InitRef();
1737 
1738 	// startup all the client stuff
1739 	CL_StartHunkUsers( qfalse );
1740 
1741 	// start the cgame if connected
1742 	if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) {
1743 		cls.cgameStarted = qtrue;
1744 		CL_InitCGame();
1745 		// send pure checksums
1746 		CL_SendPureChecksums();
1747 	}
1748 }
1749 
1750 /*
1751 =================
1752 CL_Snd_Restart
1753 
1754 Restart the sound subsystem
1755 =================
1756 */
CL_Snd_Restart(void)1757 void CL_Snd_Restart(void)
1758 {
1759 	S_Shutdown();
1760 	S_Init();
1761 }
1762 
1763 /*
1764 =================
1765 CL_Snd_Restart_f
1766 
1767 Restart the sound subsystem
1768 The cgame and game must also be forced to restart because
1769 handles will be invalid
1770 =================
1771 */
CL_Snd_Restart_f(void)1772 void CL_Snd_Restart_f(void)
1773 {
1774 	CL_Snd_Restart();
1775 	CL_Vid_Restart_f();
1776 }
1777 
1778 
1779 /*
1780 ==================
1781 CL_PK3List_f
1782 ==================
1783 */
CL_OpenedPK3List_f(void)1784 void CL_OpenedPK3List_f( void ) {
1785 	Com_Printf("Opened PK3 Names: %s\n", FS_LoadedPakNames());
1786 }
1787 
1788 /*
1789 ==================
1790 CL_PureList_f
1791 ==================
1792 */
CL_ReferencedPK3List_f(void)1793 void CL_ReferencedPK3List_f( void ) {
1794 	Com_Printf("Referenced PK3 Names: %s\n", FS_ReferencedPakNames());
1795 }
1796 
1797 /*
1798 ==================
1799 CL_Configstrings_f
1800 ==================
1801 */
CL_Configstrings_f(void)1802 void CL_Configstrings_f( void ) {
1803 	int		i;
1804 	int		ofs;
1805 
1806 	if ( cls.state != CA_ACTIVE ) {
1807 		Com_Printf( "Not connected to a server.\n");
1808 		return;
1809 	}
1810 
1811 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
1812 		ofs = cl.gameState.stringOffsets[ i ];
1813 		if ( !ofs ) {
1814 			continue;
1815 		}
1816 		Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs );
1817 	}
1818 }
1819 
1820 /*
1821 ==============
1822 CL_Clientinfo_f
1823 ==============
1824 */
CL_Clientinfo_f(void)1825 void CL_Clientinfo_f( void ) {
1826 	Com_Printf( "--------- Client Information ---------\n" );
1827 	Com_Printf( "state: %i\n", cls.state );
1828 	Com_Printf( "Server: %s\n", cls.servername );
1829 	Com_Printf ("User info settings:\n");
1830 	Info_Print( Cvar_InfoString( CVAR_USERINFO ) );
1831 	Com_Printf( "--------------------------------------\n" );
1832 }
1833 
1834 
1835 //====================================================================
1836 
1837 /*
1838 =================
1839 CL_DownloadsComplete
1840 
1841 Called when all downloading has been completed
1842 =================
1843 */
CL_DownloadsComplete(void)1844 void CL_DownloadsComplete( void ) {
1845 
1846 #ifdef USE_CURL
1847 	// if we downloaded with cURL
1848 	if(clc.cURLUsed) {
1849 		clc.cURLUsed = qfalse;
1850 		CL_cURL_Shutdown();
1851 		if( clc.cURLDisconnected ) {
1852 			if(clc.downloadRestart) {
1853 				FS_Restart(clc.checksumFeed);
1854 				clc.downloadRestart = qfalse;
1855 			}
1856 			clc.cURLDisconnected = qfalse;
1857 			CL_Reconnect_f();
1858 			return;
1859 		}
1860 	}
1861 #endif
1862 
1863 	// if we downloaded files we need to restart the file system
1864 	if (clc.downloadRestart) {
1865 		clc.downloadRestart = qfalse;
1866 
1867 		FS_Restart(clc.checksumFeed); // We possibly downloaded a pak, restart the file system to load it
1868 
1869 		// inform the server so we get new gamestate info
1870 		CL_AddReliableCommand("donedl", qfalse);
1871 
1872 		// by sending the donedl command we request a new gamestate
1873 		// so we don't want to load stuff yet
1874 		return;
1875 	}
1876 
1877 	// let the client game init and load data
1878 	cls.state = CA_LOADING;
1879 
1880 	// Pump the loop, this may change gamestate!
1881 	Com_EventLoop();
1882 
1883 	// if the gamestate was changed by calling Com_EventLoop
1884 	// then we loaded everything already and we don't want to do it again.
1885 	if ( cls.state != CA_LOADING ) {
1886 		return;
1887 	}
1888 
1889 	// starting to load a map so we get out of full screen ui mode
1890 	Cvar_Set("r_uiFullScreen", "0");
1891 
1892 	// flush client memory and start loading stuff
1893 	// this will also (re)load the UI
1894 	// if this is a local client then only the client part of the hunk
1895 	// will be cleared, note that this is done after the hunk mark has been set
1896 	CL_FlushMemory();
1897 
1898 	// initialize the CGame
1899 	cls.cgameStarted = qtrue;
1900 	CL_InitCGame();
1901 
1902 	// set pure checksums
1903 	CL_SendPureChecksums();
1904 
1905 	CL_WritePacket();
1906 	CL_WritePacket();
1907 	CL_WritePacket();
1908 }
1909 
1910 /*
1911 =================
1912 CL_BeginDownload
1913 
1914 Requests a file to download from the server.  Stores it in the current
1915 game directory.
1916 =================
1917 */
CL_BeginDownload(const char * localName,const char * remoteName)1918 void CL_BeginDownload( const char *localName, const char *remoteName ) {
1919 
1920 	Com_DPrintf("***** CL_BeginDownload *****\n"
1921 				"Localname: %s\n"
1922 				"Remotename: %s\n"
1923 				"****************************\n", localName, remoteName);
1924 
1925 	Q_strncpyz ( clc.downloadName, localName, sizeof(clc.downloadName) );
1926 	Com_sprintf( clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName );
1927 
1928 	// Set so UI gets access to it
1929 	Cvar_Set( "cl_downloadName", remoteName );
1930 	Cvar_Set( "cl_downloadSize", "0" );
1931 	Cvar_Set( "cl_downloadCount", "0" );
1932 	Cvar_SetValue( "cl_downloadTime", cls.realtime );
1933 
1934 	clc.downloadBlock = 0; // Starting new file
1935 	clc.downloadCount = 0;
1936 
1937 	CL_AddReliableCommand(va("download %s", remoteName), qfalse);
1938 }
1939 
1940 /*
1941 =================
1942 CL_NextDownload
1943 
1944 A download completed or failed
1945 =================
1946 */
CL_NextDownload(void)1947 void CL_NextDownload(void)
1948 {
1949 	char *s;
1950 	char *remoteName, *localName;
1951 	qboolean useCURL = qfalse;
1952 
1953 	// A download has finished, check whether this matches a referenced checksum
1954 	if(*clc.downloadName)
1955 	{
1956 		char *zippath = FS_BuildOSPath(Cvar_VariableString("fs_homepath"), clc.downloadName, "");
1957 		zippath[strlen(zippath)-1] = '\0';
1958 
1959 		if(!FS_CompareZipChecksum(zippath))
1960 			Com_Error(ERR_DROP, "Incorrect checksum for file: %s", clc.downloadName);
1961 	}
1962 
1963 	*clc.downloadTempName = *clc.downloadName = 0;
1964 	Cvar_Set("cl_downloadName", "");
1965 
1966 	// We are looking to start a download here
1967 	if (*clc.downloadList) {
1968 		s = clc.downloadList;
1969 
1970 		// format is:
1971 		//  @remotename@localname@remotename@localname, etc.
1972 
1973 		if (*s == '@')
1974 			s++;
1975 		remoteName = s;
1976 
1977 		if ( (s = strchr(s, '@')) == NULL ) {
1978 			CL_DownloadsComplete();
1979 			return;
1980 		}
1981 
1982 		*s++ = 0;
1983 		localName = s;
1984 		if ( (s = strchr(s, '@')) != NULL )
1985 			*s++ = 0;
1986 		else
1987 			s = localName + strlen(localName); // point at the nul byte
1988 #ifdef USE_CURL
1989 		if(!(cl_allowDownload->integer & DLF_NO_REDIRECT)) {
1990 			if(clc.sv_allowDownload & DLF_NO_REDIRECT) {
1991 				Com_Printf("WARNING: server does not "
1992 					"allow download redirection "
1993 					"(sv_allowDownload is %d)\n",
1994 					clc.sv_allowDownload);
1995 			}
1996 			else if(!*clc.sv_dlURL) {
1997 				Com_Printf("WARNING: server allows "
1998 					"download redirection, but does not "
1999 					"have sv_dlURL set\n");
2000 			}
2001 			else if(!CL_cURL_Init()) {
2002 				Com_Printf("WARNING: could not load "
2003 					"cURL library\n");
2004 			}
2005 			else {
2006 				CL_cURL_BeginDownload(localName, va("%s/%s",
2007 					clc.sv_dlURL, remoteName));
2008 				useCURL = qtrue;
2009 			}
2010 		}
2011 		else if(!(clc.sv_allowDownload & DLF_NO_REDIRECT)) {
2012 			Com_Printf("WARNING: server allows download "
2013 				"redirection, but it disabled by client "
2014 				"configuration (cl_allowDownload is %d)\n",
2015 				cl_allowDownload->integer);
2016 		}
2017 #endif /* USE_CURL */
2018 		if(!useCURL) {
2019 			if((cl_allowDownload->integer & DLF_NO_UDP)) {
2020 				Com_Error(ERR_DROP, "UDP Downloads are "
2021 					"disabled on your client. "
2022 					"(cl_allowDownload is %d)",
2023 					cl_allowDownload->integer);
2024 				return;
2025 			}
2026 			else {
2027 				CL_BeginDownload( localName, remoteName );
2028 			}
2029 		}
2030 		clc.downloadRestart = qtrue;
2031 
2032 		// move over the rest
2033 		memmove( clc.downloadList, s, strlen(s) + 1);
2034 
2035 		return;
2036 	}
2037 
2038 	CL_DownloadsComplete();
2039 }
2040 
2041 /*
2042 =================
2043 CL_InitDownloads
2044 
2045 After receiving a valid game state, we valid the cgame and local zip files here
2046 and determine if we need to download them
2047 =================
2048 */
CL_InitDownloads(void)2049 void CL_InitDownloads(void) {
2050   char missingfiles[1024];
2051 
2052   if ( !(cl_allowDownload->integer & DLF_ENABLE) )
2053   {
2054     // autodownload is disabled on the client
2055     // but it's possible that some referenced files on the server are missing
2056     if (FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) )
2057     {
2058       // NOTE TTimo I would rather have that printed as a modal message box
2059       //   but at this point while joining the game we don't know wether we will successfully join or not
2060       Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s"
2061                   "You might not be able to join the game\n"
2062                   "Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles );
2063     }
2064   }
2065   else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ) , qtrue ) ) {
2066 
2067     Com_Printf("Need paks: %s\n", clc.downloadList );
2068 
2069 		if ( *clc.downloadList ) {
2070 			// if autodownloading is not enabled on the server
2071 			cls.state = CA_CONNECTED;
2072 
2073 			*clc.downloadTempName = *clc.downloadName = 0;
2074 			Cvar_Set( "cl_downloadName", "" );
2075 
2076 			CL_NextDownload();
2077 			return;
2078 		}
2079 
2080 	}
2081 
2082 	CL_DownloadsComplete();
2083 }
2084 
2085 /*
2086 =================
2087 CL_CheckForResend
2088 
2089 Resend a connect message if the last one has timed out
2090 =================
2091 */
CL_CheckForResend(void)2092 void CL_CheckForResend( void ) {
2093 	int		port, i;
2094 	char	info[MAX_INFO_STRING];
2095 	char	data[MAX_INFO_STRING];
2096 
2097 	// don't send anything if playing back a demo
2098 	if ( clc.demoplaying ) {
2099 		return;
2100 	}
2101 
2102 	// resend if we haven't gotten a reply yet
2103 	if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) {
2104 		return;
2105 	}
2106 
2107 	if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) {
2108 		return;
2109 	}
2110 
2111 	clc.connectTime = cls.realtime;	// for retransmit requests
2112 	clc.connectPacketCount++;
2113 
2114 
2115 	switch ( cls.state ) {
2116 	case CA_CONNECTING:
2117 		// requesting a challenge .. IPv6 users always get in as authorize server supports no ipv6.
2118 #ifndef STANDALONE
2119 		if (!com_standalone->integer && clc.serverAddress.type == NA_IP && !Sys_IsLANAddress( clc.serverAddress ) )
2120 			CL_RequestAuthorization();
2121 #endif
2122 
2123 		// The challenge request shall be followed by a client challenge so no malicious server can hijack this connection.
2124 		Com_sprintf(data, sizeof(data), "getchallenge %d", clc.challenge);
2125 
2126 		NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "%s", data);
2127 		break;
2128 
2129 	case CA_CHALLENGING:
2130 		// sending back the challenge
2131 		port = Cvar_VariableValue ("net_qport");
2132 
2133 		Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
2134 		Info_SetValueForKey( info, "protocol", va("%i", com_protocol->integer ) );
2135 		Info_SetValueForKey( info, "qport", va("%i", port ) );
2136 		Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) );
2137 
2138 		strcpy(data, "connect ");
2139     // TTimo adding " " around the userinfo string to avoid truncated userinfo on the server
2140     //   (Com_TokenizeString tokenizes around spaces)
2141     data[8] = '"';
2142 
2143 		for(i=0;i<strlen(info);i++) {
2144 			data[9+i] = info[i];	// + (clc.challenge)&0x3;
2145 		}
2146     data[9+i] = '"';
2147 		data[10+i] = 0;
2148 
2149     // NOTE TTimo don't forget to set the right data length!
2150 		NET_OutOfBandData( NS_CLIENT, clc.serverAddress, (byte *) &data[0], i+10 );
2151 		// the most current userinfo has been sent, so watch for any
2152 		// newer changes to userinfo variables
2153 		cvar_modifiedFlags &= ~CVAR_USERINFO;
2154 		break;
2155 
2156 	default:
2157 		Com_Error( ERR_FATAL, "CL_CheckForResend: bad cls.state" );
2158 	}
2159 }
2160 
2161 /*
2162 ===================
2163 CL_DisconnectPacket
2164 
2165 Sometimes the server can drop the client and the netchan based
2166 disconnect can be lost.  If the client continues to send packets
2167 to the server, the server will send out of band disconnect packets
2168 to the client so it doesn't have to wait for the full timeout period.
2169 ===================
2170 */
CL_DisconnectPacket(netadr_t from)2171 void CL_DisconnectPacket( netadr_t from ) {
2172 	if ( cls.state < CA_AUTHORIZING ) {
2173 		return;
2174 	}
2175 
2176 	// if not from our server, ignore it
2177 	if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
2178 		return;
2179 	}
2180 
2181 	// if we have received packets within three seconds, ignore it
2182 	// (it might be a malicious spoof)
2183 	if ( cls.realtime - clc.lastPacketTime < 3000 ) {
2184 		return;
2185 	}
2186 
2187 	// drop the connection
2188 	Com_Printf( "Server disconnected for unknown reason\n" );
2189 	Cvar_Set("com_errorMessage", "Server disconnected for unknown reason\n" );
2190 	CL_Disconnect( qtrue );
2191 }
2192 
2193 
2194 /*
2195 ===================
2196 CL_MotdPacket
2197 
2198 ===================
2199 */
CL_MotdPacket(netadr_t from)2200 void CL_MotdPacket( netadr_t from ) {
2201 	char	*challenge;
2202 	char	*info;
2203 
2204 	// if not from our server, ignore it
2205 	if ( !NET_CompareAdr( from, cls.updateServer ) ) {
2206 		return;
2207 	}
2208 
2209 	info = Cmd_Argv(1);
2210 
2211 	// check challenge
2212 	challenge = Info_ValueForKey( info, "challenge" );
2213 	if ( strcmp( challenge, cls.updateChallenge ) ) {
2214 		return;
2215 	}
2216 
2217 	challenge = Info_ValueForKey( info, "motd" );
2218 
2219 	Q_strncpyz( cls.updateInfoString, info, sizeof( cls.updateInfoString ) );
2220 	Cvar_Set( "cl_motdString", challenge );
2221 }
2222 
2223 /*
2224 ===================
2225 CL_InitServerInfo
2226 ===================
2227 */
CL_InitServerInfo(serverInfo_t * server,netadr_t * address)2228 void CL_InitServerInfo( serverInfo_t *server, netadr_t *address ) {
2229 	server->adr = *address;
2230 	server->clients = 0;
2231 	server->hostName[0] = '\0';
2232 	server->mapName[0] = '\0';
2233 	server->maxClients = 0;
2234 	server->maxPing = 0;
2235 	server->minPing = 0;
2236 	server->ping = -1;
2237 	server->game[0] = '\0';
2238 	server->gameType = 0;
2239 	server->netType = 0;
2240 }
2241 
2242 #define MAX_SERVERSPERPACKET	256
2243 
2244 /*
2245 ===================
2246 CL_ServersResponsePacket
2247 ===================
2248 */
CL_ServersResponsePacket(const netadr_t * from,msg_t * msg,qboolean extended)2249 void CL_ServersResponsePacket( const netadr_t* from, msg_t *msg, qboolean extended ) {
2250 	int				i, j, count, total;
2251 	netadr_t addresses[MAX_SERVERSPERPACKET];
2252 	int				numservers;
2253 	byte*			buffptr;
2254 	byte*			buffend;
2255 
2256 	Com_Printf("CL_ServersResponsePacket\n");
2257 
2258 	if (cls.numglobalservers == -1) {
2259 		// state to detect lack of servers or lack of response
2260 		cls.numglobalservers = 0;
2261 		cls.numGlobalServerAddresses = 0;
2262 	}
2263 
2264 	// parse through server response string
2265 	numservers = 0;
2266 	buffptr    = msg->data;
2267 	buffend    = buffptr + msg->cursize;
2268 
2269 	// advance to initial token
2270 	do
2271 	{
2272 		if(*buffptr == '\\' || (extended && *buffptr == '/'))
2273 			break;
2274 
2275 		buffptr++;
2276 	} while (buffptr < buffend);
2277 
2278 	while (buffptr + 1 < buffend)
2279 	{
2280 		// IPv4 address
2281 		if (*buffptr == '\\')
2282 		{
2283 			buffptr++;
2284 
2285 			if (buffend - buffptr < sizeof(addresses[numservers].ip) + sizeof(addresses[numservers].port) + 1)
2286 				break;
2287 
2288 			for(i = 0; i < sizeof(addresses[numservers].ip); i++)
2289 				addresses[numservers].ip[i] = *buffptr++;
2290 
2291 			addresses[numservers].type = NA_IP;
2292 		}
2293 		// IPv6 address, if it's an extended response
2294 		else if (extended && *buffptr == '/')
2295 		{
2296 			buffptr++;
2297 
2298 			if (buffend - buffptr < sizeof(addresses[numservers].ip6) + sizeof(addresses[numservers].port) + 1)
2299 				break;
2300 
2301 			for(i = 0; i < sizeof(addresses[numservers].ip6); i++)
2302 				addresses[numservers].ip6[i] = *buffptr++;
2303 
2304 			addresses[numservers].type = NA_IP6;
2305 			addresses[numservers].scope_id = from->scope_id;
2306 		}
2307 		else
2308 			// syntax error!
2309 			break;
2310 
2311 		// parse out port
2312 		addresses[numservers].port = (*buffptr++) << 8;
2313 		addresses[numservers].port += *buffptr++;
2314 		addresses[numservers].port = BigShort( addresses[numservers].port );
2315 
2316 		// syntax check
2317 		if (*buffptr != '\\' && *buffptr != '/')
2318 			break;
2319 
2320 		numservers++;
2321 		if (numservers >= MAX_SERVERSPERPACKET)
2322 			break;
2323 	}
2324 
2325 	count = cls.numglobalservers;
2326 
2327 	for (i = 0; i < numservers && count < MAX_GLOBAL_SERVERS; i++) {
2328 		// build net address
2329 		serverInfo_t *server = &cls.globalServers[count];
2330 
2331 		// Tequila: It's possible to have sent many master server requests. Then
2332 		// we may receive many times the same addresses from the master server.
2333 		// We just avoid to add a server if it is still in the global servers list.
2334 		for (j = 0; j < count; j++)
2335 		{
2336 			if (NET_CompareAdr(cls.globalServers[j].adr, addresses[i]))
2337 				break;
2338 		}
2339 
2340 		if (j < count)
2341 			continue;
2342 
2343 		CL_InitServerInfo( server, &addresses[i] );
2344 		// advance to next slot
2345 		count++;
2346 	}
2347 
2348 	// if getting the global list
2349 	if ( count >= MAX_GLOBAL_SERVERS && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS )
2350 	{
2351 		// if we couldn't store the servers in the main list anymore
2352 		for (; i < numservers && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS; i++)
2353 		{
2354 			// just store the addresses in an additional list
2355 			cls.globalServerAddresses[cls.numGlobalServerAddresses++] = addresses[i];
2356 		}
2357 	}
2358 
2359 	cls.numglobalservers = count;
2360 	total = count + cls.numGlobalServerAddresses;
2361 
2362 	Com_Printf("%d servers parsed (total %d)\n", numservers, total);
2363 }
2364 
2365 /*
2366 =================
2367 CL_ConnectionlessPacket
2368 
2369 Responses to broadcasts, etc
2370 =================
2371 */
CL_ConnectionlessPacket(netadr_t from,msg_t * msg)2372 void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
2373 	char	*s;
2374 	char	*c;
2375 
2376 	MSG_BeginReadingOOB( msg );
2377 	MSG_ReadLong( msg );	// skip the -1
2378 
2379 	s = MSG_ReadStringLine( msg );
2380 
2381 	Cmd_TokenizeString( s );
2382 
2383 	c = Cmd_Argv(0);
2384 
2385 	Com_DPrintf ("CL packet %s: %s\n", NET_AdrToStringwPort(from), c);
2386 
2387 	// challenge from the server we are connecting to
2388 	if (!Q_stricmp(c, "challengeResponse"))
2389 	{
2390 		if (cls.state != CA_CONNECTING)
2391 		{
2392 			Com_DPrintf("Unwanted challenge response received.  Ignored.\n");
2393 			return;
2394 		}
2395 
2396 		if(!NET_CompareAdr(from, clc.serverAddress))
2397 		{
2398 			// This challenge response is not coming from the expected address.
2399 			// Check whether we have a matching client challenge to prevent
2400 			// connection hi-jacking.
2401 
2402 			c = Cmd_Argv(2);
2403 
2404 			if(!*c || atoi(c) != clc.challenge)
2405 			{
2406 				Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
2407 				return;
2408 			}
2409 		}
2410 
2411 		// start sending challenge response instead of challenge request packets
2412 		clc.challenge = atoi(Cmd_Argv(1));
2413 		cls.state = CA_CHALLENGING;
2414 		clc.connectPacketCount = 0;
2415 		clc.connectTime = -99999;
2416 
2417 		// take this address as the new server address.  This allows
2418 		// a server proxy to hand off connections to multiple servers
2419 		clc.serverAddress = from;
2420 		Com_DPrintf ("challengeResponse: %d\n", clc.challenge);
2421 		return;
2422 	}
2423 
2424 	// server connection
2425 	if ( !Q_stricmp(c, "connectResponse") ) {
2426 		if ( cls.state >= CA_CONNECTED ) {
2427 			Com_Printf ("Dup connect received.  Ignored.\n");
2428 			return;
2429 		}
2430 		if ( cls.state != CA_CHALLENGING ) {
2431 			Com_Printf ("connectResponse packet while not connecting. Ignored.\n");
2432 			return;
2433 		}
2434 		if ( !NET_CompareAdr( from, clc.serverAddress ) ) {
2435 			Com_Printf( "connectResponse from wrong address. Ignored.\n" );
2436 			return;
2437 		}
2438 		Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) );
2439 		cls.state = CA_CONNECTED;
2440 		clc.lastPacketSentTime = -9999;		// send first packet immediately
2441 		return;
2442 	}
2443 
2444 	// server responding to an info broadcast
2445 	if ( !Q_stricmp(c, "infoResponse") ) {
2446 		CL_ServerInfoPacket( from, msg );
2447 		return;
2448 	}
2449 
2450 	// server responding to a get playerlist
2451 	if ( !Q_stricmp(c, "statusResponse") ) {
2452 		CL_ServerStatusResponse( from, msg );
2453 		return;
2454 	}
2455 
2456 	// a disconnect message from the server, which will happen if the server
2457 	// dropped the connection but it is still getting packets from us
2458 	if (!Q_stricmp(c, "disconnect")) {
2459 		CL_DisconnectPacket( from );
2460 		return;
2461 	}
2462 
2463 	// echo request from server
2464 	if ( !Q_stricmp(c, "echo") ) {
2465 		NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) );
2466 		return;
2467 	}
2468 
2469 	// cd check
2470 	if ( !Q_stricmp(c, "keyAuthorize") ) {
2471 		// we don't use these now, so dump them on the floor
2472 		return;
2473 	}
2474 
2475 	// global MOTD from id
2476 	if ( !Q_stricmp(c, "motd") ) {
2477 		CL_MotdPacket( from );
2478 		return;
2479 	}
2480 
2481 	// echo request from server
2482 	if ( !Q_stricmp(c, "print") ) {
2483 		s = MSG_ReadString( msg );
2484 		Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) );
2485 		Com_Printf( "%s", s );
2486 		return;
2487 	}
2488 
2489 	// list of servers sent back by a master server (classic)
2490 	if ( !Q_strncmp(c, "getserversResponse", 18) ) {
2491 		CL_ServersResponsePacket( &from, msg, qfalse );
2492 		return;
2493 	}
2494 
2495 	// list of servers sent back by a master server (extended)
2496 	if ( !Q_strncmp(c, "getserversExtResponse", 21) ) {
2497 		CL_ServersResponsePacket( &from, msg, qtrue );
2498 		return;
2499 	}
2500 
2501 	Com_DPrintf ("Unknown connectionless packet command.\n");
2502 }
2503 
2504 
2505 /*
2506 =================
2507 CL_PacketEvent
2508 
2509 A packet has arrived from the main event loop
2510 =================
2511 */
CL_PacketEvent(netadr_t from,msg_t * msg)2512 void CL_PacketEvent( netadr_t from, msg_t *msg ) {
2513 	int		headerBytes;
2514 
2515 	clc.lastPacketTime = cls.realtime;
2516 
2517 	if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) {
2518 		CL_ConnectionlessPacket( from, msg );
2519 		return;
2520 	}
2521 
2522 	if ( cls.state < CA_CONNECTED ) {
2523 		return;		// can't be a valid sequenced packet
2524 	}
2525 
2526 	if ( msg->cursize < 4 ) {
2527 		Com_Printf ("%s: Runt packet\n", NET_AdrToStringwPort( from ));
2528 		return;
2529 	}
2530 
2531 	//
2532 	// packet from server
2533 	//
2534 	if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
2535 		Com_DPrintf ("%s:sequenced packet without connection\n"
2536 			, NET_AdrToStringwPort( from ) );
2537 		// FIXME: send a client disconnect?
2538 		return;
2539 	}
2540 
2541 	if (!CL_Netchan_Process( &clc.netchan, msg) ) {
2542 		return;		// out of order, duplicated, etc
2543 	}
2544 
2545 	// the header is different lengths for reliable and unreliable messages
2546 	headerBytes = msg->readcount;
2547 
2548 	// track the last message received so it can be returned in
2549 	// client messages, allowing the server to detect a dropped
2550 	// gamestate
2551 	clc.serverMessageSequence = LittleLong( *(int *)msg->data );
2552 
2553 	clc.lastPacketTime = cls.realtime;
2554 	CL_ParseServerMessage( msg );
2555 
2556 	//
2557 	// we don't know if it is ok to save a demo message until
2558 	// after we have parsed the frame
2559 	//
2560 	if ( clc.demorecording && !clc.demowaiting ) {
2561 		CL_WriteDemoMessage( msg, headerBytes );
2562 	}
2563 }
2564 
2565 /*
2566 ==================
2567 CL_CheckTimeout
2568 
2569 ==================
2570 */
CL_CheckTimeout(void)2571 void CL_CheckTimeout( void ) {
2572 	//
2573 	// check timeout
2574 	//
2575 	if ( ( !CL_CheckPaused() || !sv_paused->integer )
2576 		&& cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC
2577 	    && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) {
2578 		if (++cl.timeoutcount > 5) {	// timeoutcount saves debugger
2579 			Com_Printf ("\nServer connection timed out.\n");
2580 			CL_Disconnect( qtrue );
2581 			return;
2582 		}
2583 	} else {
2584 		cl.timeoutcount = 0;
2585 	}
2586 }
2587 
2588 /*
2589 ==================
2590 CL_CheckPaused
2591 Check whether client has been paused.
2592 ==================
2593 */
CL_CheckPaused(void)2594 qboolean CL_CheckPaused(void)
2595 {
2596 	// if cl_paused->modified is set, the cvar has only been changed in
2597 	// this frame. Keep paused in this frame to ensure the server doesn't
2598 	// lag behind.
2599 	if(cl_paused->integer || cl_paused->modified)
2600 		return qtrue;
2601 
2602 	return qfalse;
2603 }
2604 
2605 //============================================================================
2606 
2607 /*
2608 ==================
2609 CL_CheckUserinfo
2610 
2611 ==================
2612 */
CL_CheckUserinfo(void)2613 void CL_CheckUserinfo( void ) {
2614 	// don't add reliable commands when not yet connected
2615 	if(cls.state < CA_CHALLENGING)
2616 		return;
2617 
2618 	// don't overflow the reliable command buffer when paused
2619 	if(CL_CheckPaused())
2620 		return;
2621 
2622 	// send a reliable userinfo update if needed
2623 	if(cvar_modifiedFlags & CVAR_USERINFO)
2624 	{
2625 		cvar_modifiedFlags &= ~CVAR_USERINFO;
2626 		CL_AddReliableCommand(va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ), qfalse);
2627 	}
2628 }
2629 
2630 /*
2631 ==================
2632 CL_Frame
2633 
2634 ==================
2635 */
CL_Frame(int msec)2636 void CL_Frame ( int msec ) {
2637 
2638 	if ( !com_cl_running->integer ) {
2639 		return;
2640 	}
2641 
2642 #ifdef USE_CURL
2643 	if(clc.downloadCURLM) {
2644 		CL_cURL_PerformDownload();
2645 		// we can't process frames normally when in disconnected
2646 		// download mode since the ui vm expects cls.state to be
2647 		// CA_CONNECTED
2648 		if(clc.cURLDisconnected) {
2649 			cls.realFrametime = msec;
2650 			cls.frametime = msec;
2651 			cls.realtime += cls.frametime;
2652 			SCR_UpdateScreen();
2653 			S_Update();
2654 			Con_RunConsole();
2655 			cls.framecount++;
2656 			return;
2657 		}
2658 	}
2659 #endif
2660 
2661 	if ( cls.cddialog ) {
2662 		// bring up the cd error dialog if needed
2663 		cls.cddialog = qfalse;
2664 		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD );
2665 	} else	if ( cls.state == CA_DISCONNECTED && !( Key_GetCatcher( ) & KEYCATCH_UI )
2666 		&& !com_sv_running->integer && uivm ) {
2667 		// if disconnected, bring up the menu
2668 		S_StopAllSounds();
2669 		VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
2670 	}
2671 
2672 	// if recording an avi, lock to a fixed fps
2673 	if ( CL_VideoRecording( ) && cl_aviFrameRate->integer && msec) {
2674 		// save the current screen
2675 		if ( cls.state == CA_ACTIVE || cl_forceavidemo->integer) {
2676 			CL_TakeVideoFrame( );
2677 
2678 			// fixed time for next frame'
2679 			msec = (int)ceil( (1000.0f / cl_aviFrameRate->value) * com_timescale->value );
2680 			if (msec == 0) {
2681 				msec = 1;
2682 			}
2683 		}
2684 	}
2685 
2686 	if( cl_autoRecordDemo->integer ) {
2687 		if( cls.state == CA_ACTIVE && !clc.demorecording && !clc.demoplaying ) {
2688 			// If not recording a demo, and we should be, start one
2689 			qtime_t	now;
2690 			char		*nowString;
2691 			char		*p;
2692 			char		mapName[ MAX_QPATH ];
2693 			char		serverName[ MAX_OSPATH ];
2694 
2695 			Com_RealTime( &now );
2696 			nowString = va( "%04d%02d%02d%02d%02d%02d",
2697 					1900 + now.tm_year,
2698 					1 + now.tm_mon,
2699 					now.tm_mday,
2700 					now.tm_hour,
2701 					now.tm_min,
2702 					now.tm_sec );
2703 
2704 			Q_strncpyz( serverName, cls.servername, MAX_OSPATH );
2705 			// Replace the ":" in the address as it is not a valid
2706 			// file name character
2707 			p = strstr( serverName, ":" );
2708 			if( p ) {
2709 				*p = '.';
2710 			}
2711 
2712 			Q_strncpyz( mapName, COM_SkipPath( cl.mapname ), sizeof( cl.mapname ) );
2713 			COM_StripExtension(mapName, mapName, sizeof(mapName));
2714 
2715 			Cbuf_ExecuteText( EXEC_NOW,
2716 					va( "record %s-%s-%s", nowString, serverName, mapName ) );
2717 		}
2718 		else if( cls.state != CA_ACTIVE && clc.demorecording ) {
2719 			// Recording, but not CA_ACTIVE, so stop recording
2720 			CL_StopRecord_f( );
2721 		}
2722 	}
2723 
2724 	// save the msec before checking pause
2725 	cls.realFrametime = msec;
2726 
2727 	// decide the simulation time
2728 	cls.frametime = msec;
2729 
2730 	cls.realtime += cls.frametime;
2731 
2732 	if ( cl_timegraph->integer ) {
2733 		SCR_DebugGraph ( cls.realFrametime * 0.25, 0 );
2734 	}
2735 
2736 	// see if we need to update any userinfo
2737 	CL_CheckUserinfo();
2738 
2739 	// if we haven't gotten a packet in a long time,
2740 	// drop the connection
2741 	CL_CheckTimeout();
2742 
2743 	// send intentions now
2744 	CL_SendCmd();
2745 
2746 	// resend a connection request if necessary
2747 	CL_CheckForResend();
2748 
2749 	// decide on the serverTime to render
2750 	CL_SetCGameTime();
2751 
2752 	// update the screen
2753 	SCR_UpdateScreen();
2754 
2755 	// update audio
2756 	S_Update();
2757 
2758 #ifdef USE_VOIP
2759 	CL_CaptureVoip();
2760 #endif
2761 
2762 #ifdef USE_MUMBLE
2763 	CL_UpdateMumble();
2764 #endif
2765 
2766 	// advance local effects for next frame
2767 	SCR_RunCinematic();
2768 
2769 	Con_RunConsole();
2770 
2771 	cls.framecount++;
2772 }
2773 
2774 
2775 //============================================================================
2776 
2777 /*
2778 ================
2779 CL_RefPrintf
2780 
2781 DLL glue
2782 ================
2783 */
CL_RefPrintf(int print_level,const char * fmt,...)2784 void QDECL CL_RefPrintf( int print_level, const char *fmt, ...) {
2785 	va_list		argptr;
2786 	char		msg[MAXPRINTMSG];
2787 
2788 	va_start (argptr,fmt);
2789 	Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
2790 	va_end (argptr);
2791 
2792 	if ( print_level == PRINT_ALL ) {
2793 		Com_Printf ("%s", msg);
2794 	} else if ( print_level == PRINT_WARNING ) {
2795 		Com_Printf (S_COLOR_YELLOW "%s", msg);		// yellow
2796 	} else if ( print_level == PRINT_DEVELOPER ) {
2797 		Com_DPrintf (S_COLOR_RED "%s", msg);		// red
2798 	}
2799 }
2800 
2801 
2802 
2803 /*
2804 ============
2805 CL_ShutdownRef
2806 ============
2807 */
CL_ShutdownRef(void)2808 void CL_ShutdownRef( void ) {
2809 	if ( !re.Shutdown ) {
2810 		return;
2811 	}
2812 	re.Shutdown( qtrue );
2813 	Com_Memset( &re, 0, sizeof( re ) );
2814 }
2815 
2816 /*
2817 ============
2818 CL_InitRenderer
2819 ============
2820 */
CL_InitRenderer(void)2821 void CL_InitRenderer( void ) {
2822 	// this sets up the renderer and calls R_Init
2823 	re.BeginRegistration( &cls.glconfig );
2824 
2825 	// load character sets
2826 	cls.charSetShader = re.RegisterShader( "gfx/2d/bigchars" );
2827 	cls.whiteShader = re.RegisterShader( "white" );
2828 	cls.consoleShader = re.RegisterShader( "console" );
2829 	g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2;
2830 	g_consoleField.widthInChars = g_console_field_width;
2831 }
2832 
2833 /*
2834 ============================
2835 CL_StartHunkUsers
2836 
2837 After the server has cleared the hunk, these will need to be restarted
2838 This is the only place that any of these functions are called from
2839 ============================
2840 */
CL_StartHunkUsers(qboolean rendererOnly)2841 void CL_StartHunkUsers( qboolean rendererOnly ) {
2842 	if (!com_cl_running) {
2843 		return;
2844 	}
2845 
2846 	if ( !com_cl_running->integer ) {
2847 		return;
2848 	}
2849 
2850 	if ( !cls.rendererStarted ) {
2851 		cls.rendererStarted = qtrue;
2852 		CL_InitRenderer();
2853 	}
2854 
2855 	if ( rendererOnly ) {
2856 		return;
2857 	}
2858 
2859 	if ( !cls.soundStarted ) {
2860 		cls.soundStarted = qtrue;
2861 		S_Init();
2862 	}
2863 
2864 	if ( !cls.soundRegistered ) {
2865 		cls.soundRegistered = qtrue;
2866 		S_BeginRegistration();
2867 	}
2868 
2869 	if( com_dedicated->integer ) {
2870 		return;
2871 	}
2872 
2873 	if ( !cls.uiStarted ) {
2874 		cls.uiStarted = qtrue;
2875 		CL_InitUI();
2876 	}
2877 }
2878 
2879 /*
2880 ============
2881 CL_RefMalloc
2882 ============
2883 */
CL_RefMalloc(int size)2884 void *CL_RefMalloc( int size ) {
2885 	return Z_TagMalloc( size, TAG_RENDERER );
2886 }
2887 
CL_ScaledMilliseconds(void)2888 int CL_ScaledMilliseconds(void) {
2889 	return Sys_Milliseconds()*com_timescale->value;
2890 }
2891 
2892 /*
2893 ============
2894 CL_InitRef
2895 ============
2896 */
CL_InitRef(void)2897 void CL_InitRef( void ) {
2898 	refimport_t	ri;
2899 	refexport_t	*ret;
2900 
2901 	Com_Printf( "----- Initializing Renderer ----\n" );
2902 
2903 	ri.Cmd_AddCommand = Cmd_AddCommand;
2904 	ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
2905 	ri.Cmd_Argc = Cmd_Argc;
2906 	ri.Cmd_Argv = Cmd_Argv;
2907 	ri.Cmd_ExecuteText = Cbuf_ExecuteText;
2908 	ri.Printf = CL_RefPrintf;
2909 	ri.Error = Com_Error;
2910 	ri.Milliseconds = CL_ScaledMilliseconds;
2911 	ri.Malloc = CL_RefMalloc;
2912 	ri.Free = Z_Free;
2913 #ifdef HUNK_DEBUG
2914 	ri.Hunk_AllocDebug = Hunk_AllocDebug;
2915 #else
2916 	ri.Hunk_Alloc = Hunk_Alloc;
2917 #endif
2918 	ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory;
2919 	ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory;
2920 	ri.CM_DrawDebugSurface = CM_DrawDebugSurface;
2921 	ri.FS_ReadFile = FS_ReadFile;
2922 	ri.FS_FreeFile = FS_FreeFile;
2923 	ri.FS_WriteFile = FS_WriteFile;
2924 	ri.FS_FreeFileList = FS_FreeFileList;
2925 	ri.FS_ListFiles = FS_ListFiles;
2926 	ri.FS_FileIsInPAK = FS_FileIsInPAK;
2927 	ri.FS_FileExists = FS_FileExists;
2928 	ri.Cvar_Get = Cvar_Get;
2929 	ri.Cvar_Set = Cvar_Set;
2930 	ri.Cvar_CheckRange = Cvar_CheckRange;
2931 
2932 	// cinematic stuff
2933 
2934 	ri.CIN_UploadCinematic = CIN_UploadCinematic;
2935 	ri.CIN_PlayCinematic = CIN_PlayCinematic;
2936 	ri.CIN_RunCinematic = CIN_RunCinematic;
2937 
2938 	ri.CL_WriteAVIVideoFrame = CL_WriteAVIVideoFrame;
2939 
2940 	ret = GetRefAPI( REF_API_VERSION, &ri );
2941 
2942 #if defined __USEA3D && defined __A3D_GEOM
2943 	hA3Dg_ExportRenderGeom (ret);
2944 #endif
2945 
2946 	Com_Printf( "-------------------------------\n");
2947 
2948 	if ( !ret ) {
2949 		Com_Error (ERR_FATAL, "Couldn't initialize refresh" );
2950 	}
2951 
2952 	re = *ret;
2953 
2954 	// unpause so the cgame definately gets a snapshot and renders a frame
2955 	Cvar_Set( "cl_paused", "0" );
2956 }
2957 
2958 
2959 //===========================================================================================
2960 
2961 
CL_SetModel_f(void)2962 void CL_SetModel_f( void ) {
2963 	char	*arg;
2964 	char	name[256];
2965 
2966 	arg = Cmd_Argv( 1 );
2967 	if (arg[0]) {
2968 		Cvar_Set( "model", arg );
2969 		Cvar_Set( "headmodel", arg );
2970 	} else {
2971 		Cvar_VariableStringBuffer( "model", name, sizeof(name) );
2972 		Com_Printf("model is set to %s\n", name);
2973 	}
2974 }
2975 
2976 
2977 //===========================================================================================
2978 
2979 
2980 /*
2981 ===============
2982 CL_Video_f
2983 
2984 video
2985 video [filename]
2986 ===============
2987 */
CL_Video_f(void)2988 void CL_Video_f( void )
2989 {
2990   char  filename[ MAX_OSPATH ];
2991   int   i, last;
2992 
2993   if( !clc.demoplaying )
2994   {
2995     Com_Printf( "The video command can only be used when playing back demos\n" );
2996     return;
2997   }
2998 
2999   if( Cmd_Argc( ) == 2 )
3000   {
3001     // explicit filename
3002     Com_sprintf( filename, MAX_OSPATH, "videos/%s.avi", Cmd_Argv( 1 ) );
3003   }
3004   else
3005   {
3006     // scan for a free filename
3007     for( i = 0; i <= 9999; i++ )
3008     {
3009       int a, b, c, d;
3010 
3011       last = i;
3012 
3013       a = last / 1000;
3014       last -= a * 1000;
3015       b = last / 100;
3016       last -= b * 100;
3017       c = last / 10;
3018       last -= c * 10;
3019       d = last;
3020 
3021       Com_sprintf( filename, MAX_OSPATH, "videos/video%d%d%d%d.avi",
3022           a, b, c, d );
3023 
3024       if( !FS_FileExists( filename ) )
3025         break; // file doesn't exist
3026     }
3027 
3028     if( i > 9999 )
3029     {
3030       Com_Printf( S_COLOR_RED "ERROR: no free file names to create video\n" );
3031       return;
3032     }
3033   }
3034 
3035   CL_OpenAVIForWriting( filename );
3036 }
3037 
3038 /*
3039 ===============
3040 CL_StopVideo_f
3041 ===============
3042 */
CL_StopVideo_f(void)3043 void CL_StopVideo_f( void )
3044 {
3045   CL_CloseAVI( );
3046 }
3047 
3048 /*
3049 ===============
3050 CL_GenerateQKey
3051 
3052 test to see if a valid QKEY_FILE exists.  If one does not, try to generate
3053 it by filling it with 2048 bytes of random data.
3054 ===============
3055 */
CL_GenerateQKey(void)3056 static void CL_GenerateQKey(void)
3057 {
3058 	int len = 0;
3059 	unsigned char buff[ QKEY_SIZE ];
3060 	fileHandle_t f;
3061 
3062 	len = FS_SV_FOpenFileRead( QKEY_FILE, &f );
3063 	FS_FCloseFile( f );
3064 	if( len == QKEY_SIZE ) {
3065 		Com_Printf( "QKEY found.\n" );
3066 		return;
3067 	}
3068 	else {
3069 		if( len > 0 ) {
3070 			Com_Printf( "QKEY file size != %d, regenerating\n",
3071 				QKEY_SIZE );
3072 		}
3073 
3074 		Com_Printf( "QKEY building random string\n" );
3075 		Com_RandomBytes( buff, sizeof(buff) );
3076 
3077 		f = FS_SV_FOpenFileWrite( QKEY_FILE );
3078 		if( !f ) {
3079 			Com_Printf( "QKEY could not open %s for write\n",
3080 				QKEY_FILE );
3081 			return;
3082 		}
3083 		FS_Write( buff, sizeof(buff), f );
3084 		FS_FCloseFile( f );
3085 		Com_Printf( "QKEY generated\n" );
3086 	}
3087 }
3088 
3089 /*
3090 ====================
3091 CL_Init
3092 ====================
3093 */
CL_Init(void)3094 void CL_Init( void ) {
3095 	Com_Printf( "----- Client Initialization -----\n" );
3096 
3097 	Con_Init ();
3098 
3099 	CL_ClearState ();
3100 
3101 	cls.state = CA_DISCONNECTED;	// no longer CA_UNINITIALIZED
3102 
3103 	cls.realtime = 0;
3104 
3105 	CL_InitInput ();
3106 
3107 	//
3108 	// register our variables
3109 	//
3110 	cl_noprint = Cvar_Get( "cl_noprint", "0", 0 );
3111 	cl_motd = Cvar_Get ("cl_motd", "1", 0);
3112 
3113 	cl_timeout = Cvar_Get ("cl_timeout", "200", 0);
3114 
3115 	cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP );
3116 	cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP );
3117 	cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP );
3118 	cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP );
3119 	cl_freezeDemo = Cvar_Get ("cl_freezeDemo", "0", CVAR_TEMP );
3120 	rcon_client_password = Cvar_Get ("rconPassword", "", CVAR_TEMP );
3121 	cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP );
3122 
3123 	cl_timedemo = Cvar_Get ("timedemo", "0", 0);
3124 	cl_timedemoLog = Cvar_Get ("cl_timedemoLog", "", CVAR_ARCHIVE);
3125 	cl_autoRecordDemo = Cvar_Get ("cl_autoRecordDemo", "0", CVAR_ARCHIVE);
3126 	cl_aviFrameRate = Cvar_Get ("cl_aviFrameRate", "25", CVAR_ARCHIVE);
3127 	cl_aviMotionJpeg = Cvar_Get ("cl_aviMotionJpeg", "1", CVAR_ARCHIVE);
3128 	cl_forceavidemo = Cvar_Get ("cl_forceavidemo", "0", 0);
3129 
3130 	rconAddress = Cvar_Get ("rconAddress", "", 0);
3131 
3132 	//cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE);
3133 	//cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE);
3134 	cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
3135 
3136 	cl_maxpackets = Cvar_Get ("cl_maxpackets", "30", CVAR_ARCHIVE );
3137 	cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE );
3138 
3139 	cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE);
3140 	cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE);
3141 	cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE);
3142 	cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE );
3143 
3144 	// 0: legacy mouse acceleration
3145 	// 1: new implementation
3146 	cl_mouseAccelStyle = Cvar_Get( "cl_mouseAccelStyle", "0", CVAR_ARCHIVE );
3147 	// offset for the power function (for style 1, ignored otherwise)
3148 	// this should be set to the max rate value
3149 	cl_mouseAccelOffset = Cvar_Get( "cl_mouseAccelOffset", "5", CVAR_ARCHIVE );
3150 	Cvar_CheckRange(cl_mouseAccelOffset, 0.001f, 50000.0f, qfalse);
3151 
3152 	cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0);
3153 
3154 	cl_allowDownload = Cvar_Get ("cl_allowDownload", "0", CVAR_ARCHIVE);
3155 #ifdef USE_CURL
3156 	cl_cURLLib = Cvar_Get("cl_cURLLib", DEFAULT_CURL_LIB, CVAR_ARCHIVE);
3157 #endif
3158 
3159 	cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0);
3160 #ifdef MACOS_X
3161 	// In game video is REALLY slow in Mac OS X right now due to driver slowness
3162 	cl_inGameVideo = Cvar_Get ("r_inGameVideo", "0", CVAR_ARCHIVE);
3163 #else
3164 	cl_inGameVideo = Cvar_Get ("r_inGameVideo", "1", CVAR_ARCHIVE);
3165 #endif
3166 
3167 	cl_serverStatusResendTime = Cvar_Get ("cl_serverStatusResendTime", "750", 0);
3168 
3169 	// init autoswitch so the ui will have it correctly even
3170 	// if the cgame hasn't been started
3171 	Cvar_Get ("cg_autoswitch", "2", CVAR_ARCHIVE);
3172 
3173 	m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
3174 	m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE);
3175 	m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE);
3176 	m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE);
3177 #ifdef MACOS_X
3178 	// Input is jittery on OS X w/o this
3179 	m_filter = Cvar_Get ("m_filter", "1", CVAR_ARCHIVE);
3180 #else
3181 	m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE);
3182 #endif
3183 
3184 	cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM );
3185 
3186 	Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE );
3187 
3188 	cl_lanForcePackets = Cvar_Get ("cl_lanForcePackets", "1", CVAR_ARCHIVE);
3189 
3190 	cl_guidServerUniq = Cvar_Get ("cl_guidServerUniq", "1", CVAR_ARCHIVE);
3191 
3192 	// ~ and `, as keys and characters
3193 	cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60", CVAR_ARCHIVE);
3194 
3195 	cl_consoleType = Cvar_Get( "cl_consoleType", "0", CVAR_ARCHIVE );
3196 	cl_consoleColor[0] = Cvar_Get( "cl_consoleColorRed", "1", CVAR_ARCHIVE );
3197 	cl_consoleColor[1] = Cvar_Get( "cl_consoleColorGreen", "0", CVAR_ARCHIVE );
3198 	cl_consoleColor[2] = Cvar_Get( "cl_consoleColorBlue", "0", CVAR_ARCHIVE );
3199 	cl_consoleColor[3] = Cvar_Get( "cl_consoleColorAlpha", "0.8", CVAR_ARCHIVE );
3200 
3201 	cl_consoleHeight = Cvar_Get("cl_consoleHeight", "0.5", CVAR_ARCHIVE);
3202 
3203 	cl_gamename = Cvar_Get("cl_gamename", GAMENAME_FOR_MASTER, CVAR_TEMP);
3204 
3205 	// userinfo
3206 	Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE );
3207 	Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE );
3208 	Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE );
3209 	Cvar_Get ("model", "sarge", CVAR_USERINFO | CVAR_ARCHIVE );
3210 	Cvar_Get ("headmodel", "sarge", CVAR_USERINFO | CVAR_ARCHIVE );
3211 	Cvar_Get ("team_model", "james", CVAR_USERINFO | CVAR_ARCHIVE );
3212 	Cvar_Get ("team_headmodel", "*james", CVAR_USERINFO | CVAR_ARCHIVE );
3213 	Cvar_Get ("g_redTeam", "Stroggs", CVAR_SERVERINFO | CVAR_ARCHIVE);
3214 	Cvar_Get ("g_blueTeam", "Pagans", CVAR_SERVERINFO | CVAR_ARCHIVE);
3215 	Cvar_Get ("color1",  "4", CVAR_USERINFO | CVAR_ARCHIVE );
3216 	Cvar_Get ("color2", "5", CVAR_USERINFO | CVAR_ARCHIVE );
3217 	Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE );
3218 	Cvar_Get ("teamtask", "0", CVAR_USERINFO );
3219 	Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE );
3220 	Cvar_Get ("cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE );
3221 
3222 	Cvar_Get ("password", "", CVAR_USERINFO);
3223 	Cvar_Get ("cg_predictItems", "1", CVAR_USERINFO | CVAR_ARCHIVE );
3224 
3225 #ifdef USE_MUMBLE
3226 	cl_useMumble = Cvar_Get ("cl_useMumble", "0", CVAR_ARCHIVE | CVAR_LATCH);
3227 	cl_mumbleScale = Cvar_Get ("cl_mumbleScale", "0.0254", CVAR_ARCHIVE);
3228 #endif
3229 
3230 #ifdef USE_VOIP
3231 	cl_voipSend = Cvar_Get ("cl_voipSend", "0", 0);
3232 	cl_voipSendTarget = Cvar_Get ("cl_voipSendTarget", "all", 0);
3233 	cl_voipGainDuringCapture = Cvar_Get ("cl_voipGainDuringCapture", "0.2", CVAR_ARCHIVE);
3234 	cl_voipCaptureMult = Cvar_Get ("cl_voipCaptureMult", "2.0", CVAR_ARCHIVE);
3235 	cl_voipUseVAD = Cvar_Get ("cl_voipUseVAD", "0", CVAR_ARCHIVE);
3236 	cl_voipVADThreshold = Cvar_Get ("cl_voipVADThreshold", "0.25", CVAR_ARCHIVE);
3237 	cl_voipShowMeter = Cvar_Get ("cl_voipShowMeter", "1", CVAR_ARCHIVE);
3238 
3239 	// This is a protocol version number.
3240 	cl_voip = Cvar_Get ("cl_voip", "1", CVAR_USERINFO | CVAR_ARCHIVE | CVAR_LATCH);
3241 	Cvar_CheckRange( cl_voip, 0, 1, qtrue );
3242 
3243 	// If your data rate is too low, you'll get Connection Interrupted warnings
3244 	//  when VoIP packets arrive, even if you have a broadband connection.
3245 	//  This might work on rates lower than 25000, but for safety's sake, we'll
3246 	//  just demand it. Who doesn't have at least a DSL line now, anyhow? If
3247 	//  you don't, you don't need VoIP.  :)
3248 	if ((cl_voip->integer) && (Cvar_VariableIntegerValue("rate") < 25000)) {
3249 		Com_Printf("Your network rate is too slow for VoIP.\n");
3250 		Com_Printf("Set 'Data Rate' to 'LAN/Cable/xDSL' in 'Setup/System/Network' and restart.\n");
3251 		Com_Printf("Until then, VoIP is disabled.\n");
3252 		Cvar_Set("cl_voip", "0");
3253 	}
3254 #endif
3255 
3256 
3257 	// cgame might not be initialized before menu is used
3258 	Cvar_Get ("cg_viewsize", "100", CVAR_ARCHIVE );
3259 	// Make sure cg_stereoSeparation is zero as that variable is deprecated and should not be used anymore.
3260 	Cvar_Get ("cg_stereoSeparation", "0", CVAR_ROM);
3261 
3262 	//
3263 	// register our commands
3264 	//
3265 	Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
3266 	Cmd_AddCommand ("configstrings", CL_Configstrings_f);
3267 	Cmd_AddCommand ("clientinfo", CL_Clientinfo_f);
3268 	Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
3269 	Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f);
3270 	Cmd_AddCommand ("disconnect", CL_Disconnect_f);
3271 	Cmd_AddCommand ("record", CL_Record_f);
3272 	Cmd_AddCommand ("demo", CL_PlayDemo_f);
3273 	Cmd_SetCommandCompletionFunc( "demo", CL_CompleteDemoName );
3274 	Cmd_AddCommand ("cinematic", CL_PlayCinematic_f);
3275 	Cmd_AddCommand ("stoprecord", CL_StopRecord_f);
3276 	Cmd_AddCommand ("connect", CL_Connect_f);
3277 	Cmd_AddCommand ("reconnect", CL_Reconnect_f);
3278 	Cmd_AddCommand ("localservers", CL_LocalServers_f);
3279 	Cmd_AddCommand ("globalservers", CL_GlobalServers_f);
3280 	Cmd_AddCommand ("rcon", CL_Rcon_f);
3281 	Cmd_SetCommandCompletionFunc( "rcon", CL_CompleteRcon );
3282 	Cmd_AddCommand ("ping", CL_Ping_f );
3283 	Cmd_AddCommand ("serverstatus", CL_ServerStatus_f );
3284 	Cmd_AddCommand ("showip", CL_ShowIP_f );
3285 	Cmd_AddCommand ("fs_openedList", CL_OpenedPK3List_f );
3286 	Cmd_AddCommand ("fs_referencedList", CL_ReferencedPK3List_f );
3287 	Cmd_AddCommand ("model", CL_SetModel_f );
3288 	Cmd_AddCommand ("video", CL_Video_f );
3289 	Cmd_AddCommand ("stopvideo", CL_StopVideo_f );
3290 	Cmd_AddCommand("minimize", GLimp_Minimize);
3291 	CL_InitRef();
3292 
3293 	SCR_Init ();
3294 
3295 //	Cbuf_Execute ();
3296 
3297 	Cvar_Set( "cl_running", "1" );
3298 
3299 	CL_GenerateQKey();
3300 	Cvar_Get( "cl_guid", "", CVAR_USERINFO | CVAR_ROM );
3301 	CL_UpdateGUID( NULL, 0 );
3302 
3303 	Com_Printf( "----- Client Initialization Complete -----\n" );
3304 }
3305 
3306 
3307 /*
3308 ===============
3309 CL_Shutdown
3310 
3311 ===============
3312 */
CL_Shutdown(char * finalmsg)3313 void CL_Shutdown( char *finalmsg ) {
3314 	static qboolean recursive = qfalse;
3315 
3316 	// check whether the client is running at all.
3317 	if(!(com_cl_running && com_cl_running->integer))
3318 		return;
3319 
3320 	Com_Printf( "----- Client Shutdown (%s) -----\n", finalmsg );
3321 
3322 	if ( recursive ) {
3323 		Com_Printf( "WARNING: Recursive shutdown\n" );
3324 		return;
3325 	}
3326 	recursive = qtrue;
3327 
3328 	CL_Disconnect( qtrue );
3329 
3330 	S_Shutdown();
3331 	CL_ShutdownRef();
3332 
3333 	CL_ShutdownUI();
3334 
3335 	Cmd_RemoveCommand ("cmd");
3336 	Cmd_RemoveCommand ("configstrings");
3337 	Cmd_RemoveCommand ("userinfo");
3338 	Cmd_RemoveCommand ("snd_restart");
3339 	Cmd_RemoveCommand ("vid_restart");
3340 	Cmd_RemoveCommand ("disconnect");
3341 	Cmd_RemoveCommand ("record");
3342 	Cmd_RemoveCommand ("demo");
3343 	Cmd_RemoveCommand ("cinematic");
3344 	Cmd_RemoveCommand ("stoprecord");
3345 	Cmd_RemoveCommand ("connect");
3346 	Cmd_RemoveCommand ("localservers");
3347 	Cmd_RemoveCommand ("globalservers");
3348 	Cmd_RemoveCommand ("rcon");
3349 	Cmd_RemoveCommand ("ping");
3350 	Cmd_RemoveCommand ("serverstatus");
3351 	Cmd_RemoveCommand ("showip");
3352 	Cmd_RemoveCommand ("model");
3353 	Cmd_RemoveCommand ("video");
3354 	Cmd_RemoveCommand ("stopvideo");
3355 
3356 	Cvar_Set( "cl_running", "0" );
3357 
3358 	recursive = qfalse;
3359 
3360 	Com_Memset( &cls, 0, sizeof( cls ) );
3361 	Key_SetCatcher( 0 );
3362 
3363 	Com_Printf( "-----------------------\n" );
3364 
3365 }
3366 
CL_SetServerInfo(serverInfo_t * server,const char * info,int ping)3367 static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) {
3368 	if (server) {
3369 		if (info) {
3370 			server->clients = atoi(Info_ValueForKey(info, "clients"));
3371 			Q_strncpyz(server->hostName,Info_ValueForKey(info, "hostname"), MAX_NAME_LENGTH);
3372 			Q_strncpyz(server->mapName, Info_ValueForKey(info, "mapname"), MAX_NAME_LENGTH);
3373 			server->maxClients = atoi(Info_ValueForKey(info, "sv_maxclients"));
3374 			Q_strncpyz(server->game,Info_ValueForKey(info, "game"), MAX_NAME_LENGTH);
3375 			server->gameType = atoi(Info_ValueForKey(info, "gametype"));
3376 			server->netType = atoi(Info_ValueForKey(info, "nettype"));
3377 			server->minPing = atoi(Info_ValueForKey(info, "minping"));
3378 			server->maxPing = atoi(Info_ValueForKey(info, "maxping"));
3379 			server->g_humanplayers = atoi(Info_ValueForKey(info, "g_humanplayers"));
3380 			server->g_needpass = atoi(Info_ValueForKey(info, "g_needpass"));
3381 		}
3382 		server->ping = ping;
3383 	}
3384 }
3385 
CL_SetServerInfoByAddress(netadr_t from,const char * info,int ping)3386 static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping) {
3387 	int i;
3388 
3389 	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
3390 		if (NET_CompareAdr(from, cls.localServers[i].adr)) {
3391 			CL_SetServerInfo(&cls.localServers[i], info, ping);
3392 		}
3393 	}
3394 
3395 	for (i = 0; i < MAX_GLOBAL_SERVERS; i++) {
3396 		if (NET_CompareAdr(from, cls.globalServers[i].adr)) {
3397 			CL_SetServerInfo(&cls.globalServers[i], info, ping);
3398 		}
3399 	}
3400 
3401 	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
3402 		if (NET_CompareAdr(from, cls.favoriteServers[i].adr)) {
3403 			CL_SetServerInfo(&cls.favoriteServers[i], info, ping);
3404 		}
3405 	}
3406 
3407 }
3408 
3409 /*
3410 ===================
3411 CL_ServerInfoPacket
3412 ===================
3413 */
CL_ServerInfoPacket(netadr_t from,msg_t * msg)3414 void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
3415 	int		i, type;
3416 	char	info[MAX_INFO_STRING];
3417 	char	*infoString;
3418 	int		prot;
3419 
3420 	infoString = MSG_ReadString( msg );
3421 
3422 	// if this isn't the correct protocol version, ignore it
3423 	prot = atoi( Info_ValueForKey( infoString, "protocol" ) );
3424 	if ( prot != com_protocol->integer ) {
3425 		Com_DPrintf( "Different protocol info packet: %s\n", infoString );
3426 		return;
3427 	}
3428 
3429 	// iterate servers waiting for ping response
3430 	for (i=0; i<MAX_PINGREQUESTS; i++)
3431 	{
3432 		if ( cl_pinglist[i].adr.port && !cl_pinglist[i].time && NET_CompareAdr( from, cl_pinglist[i].adr ) )
3433 		{
3434 			// calc ping time
3435 			cl_pinglist[i].time = Sys_Milliseconds() - cl_pinglist[i].start;
3436 			Com_DPrintf( "ping time %dms from %s\n", cl_pinglist[i].time, NET_AdrToString( from ) );
3437 
3438 			// save of info
3439 			Q_strncpyz( cl_pinglist[i].info, infoString, sizeof( cl_pinglist[i].info ) );
3440 
3441 			// tack on the net type
3442 			// NOTE: make sure these types are in sync with the netnames strings in the UI
3443 			switch (from.type)
3444 			{
3445 				case NA_BROADCAST:
3446 				case NA_IP:
3447 					type = 1;
3448 					break;
3449 				case NA_IP6:
3450 					type = 2;
3451 					break;
3452 				default:
3453 					type = 0;
3454 					break;
3455 			}
3456 			Info_SetValueForKey( cl_pinglist[i].info, "nettype", va("%d", type) );
3457 			CL_SetServerInfoByAddress(from, infoString, cl_pinglist[i].time);
3458 
3459 			return;
3460 		}
3461 	}
3462 
3463 	// if not just sent a local broadcast or pinging local servers
3464 	if (cls.pingUpdateSource != AS_LOCAL) {
3465 		return;
3466 	}
3467 
3468 	for ( i = 0 ; i < MAX_OTHER_SERVERS ; i++ ) {
3469 		// empty slot
3470 		if ( cls.localServers[i].adr.port == 0 ) {
3471 			break;
3472 		}
3473 
3474 		// avoid duplicate
3475 		if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) {
3476 			return;
3477 		}
3478 	}
3479 
3480 	if ( i == MAX_OTHER_SERVERS ) {
3481 		Com_DPrintf( "MAX_OTHER_SERVERS hit, dropping infoResponse\n" );
3482 		return;
3483 	}
3484 
3485 	// add this to the list
3486 	cls.numlocalservers = i+1;
3487 	cls.localServers[i].adr = from;
3488 	cls.localServers[i].clients = 0;
3489 	cls.localServers[i].hostName[0] = '\0';
3490 	cls.localServers[i].mapName[0] = '\0';
3491 	cls.localServers[i].maxClients = 0;
3492 	cls.localServers[i].maxPing = 0;
3493 	cls.localServers[i].minPing = 0;
3494 	cls.localServers[i].ping = -1;
3495 	cls.localServers[i].game[0] = '\0';
3496 	cls.localServers[i].gameType = 0;
3497 	cls.localServers[i].netType = from.type;
3498 
3499 	Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING );
3500 	if (strlen(info)) {
3501 		if (info[strlen(info)-1] != '\n') {
3502 			strncat(info, "\n", sizeof(info) - 1);
3503 		}
3504 		Com_Printf( "%s: %s", NET_AdrToStringwPort( from ), info );
3505 	}
3506 }
3507 
3508 /*
3509 ===================
3510 CL_GetServerStatus
3511 ===================
3512 */
CL_GetServerStatus(netadr_t from)3513 serverStatus_t *CL_GetServerStatus( netadr_t from ) {
3514 	serverStatus_t *serverStatus;
3515 	int i, oldest, oldestTime;
3516 
3517 	serverStatus = NULL;
3518 	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3519 		if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
3520 			return &cl_serverStatusList[i];
3521 		}
3522 	}
3523 	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3524 		if ( cl_serverStatusList[i].retrieved ) {
3525 			return &cl_serverStatusList[i];
3526 		}
3527 	}
3528 	oldest = -1;
3529 	oldestTime = 0;
3530 	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3531 		if (oldest == -1 || cl_serverStatusList[i].startTime < oldestTime) {
3532 			oldest = i;
3533 			oldestTime = cl_serverStatusList[i].startTime;
3534 		}
3535 	}
3536 	if (oldest != -1) {
3537 		return &cl_serverStatusList[oldest];
3538 	}
3539 	serverStatusCount++;
3540 	return &cl_serverStatusList[serverStatusCount & (MAX_SERVERSTATUSREQUESTS-1)];
3541 }
3542 
3543 /*
3544 ===================
3545 CL_ServerStatus
3546 ===================
3547 */
CL_ServerStatus(char * serverAddress,char * serverStatusString,int maxLen)3548 int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ) {
3549 	int i;
3550 	netadr_t	to;
3551 	serverStatus_t *serverStatus;
3552 
3553 	// if no server address then reset all server status requests
3554 	if ( !serverAddress ) {
3555 		for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3556 			cl_serverStatusList[i].address.port = 0;
3557 			cl_serverStatusList[i].retrieved = qtrue;
3558 		}
3559 		return qfalse;
3560 	}
3561 	// get the address
3562 	if ( !NET_StringToAdr( serverAddress, &to, NA_UNSPEC) ) {
3563 		return qfalse;
3564 	}
3565 	serverStatus = CL_GetServerStatus( to );
3566 	// if no server status string then reset the server status request for this address
3567 	if ( !serverStatusString ) {
3568 		serverStatus->retrieved = qtrue;
3569 		return qfalse;
3570 	}
3571 
3572 	// if this server status request has the same address
3573 	if ( NET_CompareAdr( to, serverStatus->address) ) {
3574 		// if we recieved an response for this server status request
3575 		if (!serverStatus->pending) {
3576 			Q_strncpyz(serverStatusString, serverStatus->string, maxLen);
3577 			serverStatus->retrieved = qtrue;
3578 			serverStatus->startTime = 0;
3579 			return qtrue;
3580 		}
3581 		// resend the request regularly
3582 		else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) {
3583 			serverStatus->print = qfalse;
3584 			serverStatus->pending = qtrue;
3585 			serverStatus->retrieved = qfalse;
3586 			serverStatus->time = 0;
3587 			serverStatus->startTime = Com_Milliseconds();
3588 			NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
3589 			return qfalse;
3590 		}
3591 	}
3592 	// if retrieved
3593 	else if ( serverStatus->retrieved ) {
3594 		serverStatus->address = to;
3595 		serverStatus->print = qfalse;
3596 		serverStatus->pending = qtrue;
3597 		serverStatus->retrieved = qfalse;
3598 		serverStatus->startTime = Com_Milliseconds();
3599 		serverStatus->time = 0;
3600 		NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
3601 		return qfalse;
3602 	}
3603 	return qfalse;
3604 }
3605 
3606 /*
3607 ===================
3608 CL_ServerStatusResponse
3609 ===================
3610 */
CL_ServerStatusResponse(netadr_t from,msg_t * msg)3611 void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) {
3612 	char	*s;
3613 	char	info[MAX_INFO_STRING];
3614 	int		i, l, score, ping;
3615 	int		len;
3616 	serverStatus_t *serverStatus;
3617 
3618 	serverStatus = NULL;
3619 	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3620 		if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
3621 			serverStatus = &cl_serverStatusList[i];
3622 			break;
3623 		}
3624 	}
3625 	// if we didn't request this server status
3626 	if (!serverStatus) {
3627 		return;
3628 	}
3629 
3630 	s = MSG_ReadStringLine( msg );
3631 
3632 	len = 0;
3633 	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "%s", s);
3634 
3635 	if (serverStatus->print) {
3636 		Com_Printf("Server settings:\n");
3637 		// print cvars
3638 		while (*s) {
3639 			for (i = 0; i < 2 && *s; i++) {
3640 				if (*s == '\\')
3641 					s++;
3642 				l = 0;
3643 				while (*s) {
3644 					info[l++] = *s;
3645 					if (l >= MAX_INFO_STRING-1)
3646 						break;
3647 					s++;
3648 					if (*s == '\\') {
3649 						break;
3650 					}
3651 				}
3652 				info[l] = '\0';
3653 				if (i) {
3654 					Com_Printf("%s\n", info);
3655 				}
3656 				else {
3657 					Com_Printf("%-24s", info);
3658 				}
3659 			}
3660 		}
3661 	}
3662 
3663 	len = strlen(serverStatus->string);
3664 	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
3665 
3666 	if (serverStatus->print) {
3667 		Com_Printf("\nPlayers:\n");
3668 		Com_Printf("score: ping: name:\n");
3669 	}
3670 	for (i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++) {
3671 
3672 		len = strlen(serverStatus->string);
3673 		Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\%s", s);
3674 
3675 		if (serverStatus->print) {
3676 			score = ping = 0;
3677 			sscanf(s, "%d %d", &score, &ping);
3678 			s = strchr(s, ' ');
3679 			if (s)
3680 				s = strchr(s+1, ' ');
3681 			if (s)
3682 				s++;
3683 			else
3684 				s = "unknown";
3685 			Com_Printf("%-2d %-3d    %-3d   %s\n", i, score, ping, s );
3686 		}
3687 	}
3688 	len = strlen(serverStatus->string);
3689 	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
3690 
3691 	serverStatus->time = Com_Milliseconds();
3692 	serverStatus->address = from;
3693 	serverStatus->pending = qfalse;
3694 	if (serverStatus->print) {
3695 		serverStatus->retrieved = qtrue;
3696 	}
3697 }
3698 
3699 /*
3700 ==================
3701 CL_LocalServers_f
3702 ==================
3703 */
CL_LocalServers_f(void)3704 void CL_LocalServers_f( void ) {
3705 	char		*message;
3706 	int			i, j;
3707 	netadr_t	to;
3708 
3709 	Com_Printf( "Scanning for servers on the local network...\n");
3710 
3711 	// reset the list, waiting for response
3712 	cls.numlocalservers = 0;
3713 	cls.pingUpdateSource = AS_LOCAL;
3714 
3715 	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
3716 		qboolean b = cls.localServers[i].visible;
3717 		Com_Memset(&cls.localServers[i], 0, sizeof(cls.localServers[i]));
3718 		cls.localServers[i].visible = b;
3719 	}
3720 	Com_Memset( &to, 0, sizeof( to ) );
3721 
3722 	// The 'xxx' in the message is a challenge that will be echoed back
3723 	// by the server.  We don't care about that here, but master servers
3724 	// can use that to prevent spoofed server responses from invalid ip
3725 	message = "\377\377\377\377getinfo xxx";
3726 
3727 	// send each message twice in case one is dropped
3728 	for ( i = 0 ; i < 2 ; i++ ) {
3729 		// send a broadcast packet on each server port
3730 		// we support multiple server ports so a single machine
3731 		// can nicely run multiple servers
3732 		for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) {
3733 			to.port = BigShort( (short)(PORT_SERVER + j) );
3734 
3735 			to.type = NA_BROADCAST;
3736 			NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
3737 			to.type = NA_MULTICAST6;
3738 			NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
3739 		}
3740 	}
3741 }
3742 
3743 /*
3744 ==================
3745 CL_GlobalServers_f
3746 ==================
3747 */
CL_GlobalServers_f(void)3748 void CL_GlobalServers_f( void ) {
3749 	netadr_t	to;
3750 	int			count, i, masterNum;
3751 	char		command[1024], *masteraddress;
3752 
3753 	if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > MAX_MASTER_SERVERS - 1)
3754 	{
3755 		Com_Printf("usage: globalservers <master# 0-%d> <protocol> [keywords]\n", MAX_MASTER_SERVERS - 1);
3756 		return;
3757 	}
3758 
3759 	sprintf(command, "sv_master%d", masterNum + 1);
3760 	masteraddress = Cvar_VariableString(command);
3761 
3762 	if(!*masteraddress)
3763 	{
3764 		Com_Printf( "CL_GlobalServers_f: Error: No master server address given.\n");
3765 		return;
3766 	}
3767 
3768 	// reset the list, waiting for response
3769 	// -1 is used to distinguish a "no response"
3770 
3771 	i = NET_StringToAdr(masteraddress, &to, NA_UNSPEC);
3772 
3773 	if(!i)
3774 	{
3775 		Com_Printf( "CL_GlobalServers_f: Error: could not resolve address of master %s\n", masteraddress);
3776 		return;
3777 	}
3778 	else if(i == 2)
3779 		to.port = BigShort(PORT_MASTER);
3780 
3781 	Com_Printf("Requesting servers from master %s...\n", masteraddress);
3782 
3783 	cls.numglobalservers = -1;
3784 	cls.pingUpdateSource = AS_GLOBAL;
3785 
3786 	// Use the extended query for IPv6 masters
3787 	if (to.type == NA_IP6 || to.type == NA_MULTICAST6)
3788 	{
3789 		int v4enabled = Cvar_VariableIntegerValue("net_enabled") & NET_ENABLEV4;
3790 
3791 		if(v4enabled)
3792 		{
3793 			Com_sprintf(command, sizeof(command), "getserversExt %s %s ipv6",
3794 				cl_gamename->string, Cmd_Argv(2));
3795 		}
3796 		else
3797 		{
3798 			Com_sprintf(command, sizeof(command), "getserversExt %s %s",
3799 				cl_gamename->string, Cmd_Argv(2));
3800 		}
3801 
3802 		// TODO: test if we only have an IPv6 connection. If it's the case,
3803 		//       request IPv6 servers only by appending " ipv6" to the command
3804 	}
3805 	else
3806 		Com_sprintf(command, sizeof(command), "getservers %s", Cmd_Argv(2));
3807 
3808 	for (i=3; i < count; i++)
3809 	{
3810 		Q_strcat(command, sizeof(command), " ");
3811 		Q_strcat(command, sizeof(command), Cmd_Argv(i));
3812 	}
3813 
3814 	NET_OutOfBandPrint( NS_SERVER, to, "%s", command );
3815 }
3816 
3817 
3818 /*
3819 ==================
3820 CL_GetPing
3821 ==================
3822 */
CL_GetPing(int n,char * buf,int buflen,int * pingtime)3823 void CL_GetPing( int n, char *buf, int buflen, int *pingtime )
3824 {
3825 	const char	*str;
3826 	int		time;
3827 	int		maxPing;
3828 
3829 	if (n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port)
3830 	{
3831 		// empty or invalid slot
3832 		buf[0]    = '\0';
3833 		*pingtime = 0;
3834 		return;
3835 	}
3836 
3837 	str = NET_AdrToStringwPort( cl_pinglist[n].adr );
3838 	Q_strncpyz( buf, str, buflen );
3839 
3840 	time = cl_pinglist[n].time;
3841 	if (!time)
3842 	{
3843 		// check for timeout
3844 		time = Sys_Milliseconds() - cl_pinglist[n].start;
3845 		maxPing = Cvar_VariableIntegerValue( "cl_maxPing" );
3846 		if( maxPing < 100 ) {
3847 			maxPing = 100;
3848 		}
3849 		if (time < maxPing)
3850 		{
3851 			// not timed out yet
3852 			time = 0;
3853 		}
3854 	}
3855 
3856 	CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time);
3857 
3858 	*pingtime = time;
3859 }
3860 
3861 /*
3862 ==================
3863 CL_GetPingInfo
3864 ==================
3865 */
CL_GetPingInfo(int n,char * buf,int buflen)3866 void CL_GetPingInfo( int n, char *buf, int buflen )
3867 {
3868 	if (n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port)
3869 	{
3870 		// empty or invalid slot
3871 		if (buflen)
3872 			buf[0] = '\0';
3873 		return;
3874 	}
3875 
3876 	Q_strncpyz( buf, cl_pinglist[n].info, buflen );
3877 }
3878 
3879 /*
3880 ==================
3881 CL_ClearPing
3882 ==================
3883 */
CL_ClearPing(int n)3884 void CL_ClearPing( int n )
3885 {
3886 	if (n < 0 || n >= MAX_PINGREQUESTS)
3887 		return;
3888 
3889 	cl_pinglist[n].adr.port = 0;
3890 }
3891 
3892 /*
3893 ==================
3894 CL_GetPingQueueCount
3895 ==================
3896 */
CL_GetPingQueueCount(void)3897 int CL_GetPingQueueCount( void )
3898 {
3899 	int		i;
3900 	int		count;
3901 	ping_t*	pingptr;
3902 
3903 	count   = 0;
3904 	pingptr = cl_pinglist;
3905 
3906 	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ ) {
3907 		if (pingptr->adr.port) {
3908 			count++;
3909 		}
3910 	}
3911 
3912 	return (count);
3913 }
3914 
3915 /*
3916 ==================
3917 CL_GetFreePing
3918 ==================
3919 */
CL_GetFreePing(void)3920 ping_t* CL_GetFreePing( void )
3921 {
3922 	ping_t*	pingptr;
3923 	ping_t*	best;
3924 	int		oldest;
3925 	int		i;
3926 	int		time;
3927 
3928 	pingptr = cl_pinglist;
3929 	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
3930 	{
3931 		// find free ping slot
3932 		if (pingptr->adr.port)
3933 		{
3934 			if (!pingptr->time)
3935 			{
3936 				if (Sys_Milliseconds() - pingptr->start < 500)
3937 				{
3938 					// still waiting for response
3939 					continue;
3940 				}
3941 			}
3942 			else if (pingptr->time < 500)
3943 			{
3944 				// results have not been queried
3945 				continue;
3946 			}
3947 		}
3948 
3949 		// clear it
3950 		pingptr->adr.port = 0;
3951 		return (pingptr);
3952 	}
3953 
3954 	// use oldest entry
3955 	pingptr = cl_pinglist;
3956 	best    = cl_pinglist;
3957 	oldest  = INT_MIN;
3958 	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
3959 	{
3960 		// scan for oldest
3961 		time = Sys_Milliseconds() - pingptr->start;
3962 		if (time > oldest)
3963 		{
3964 			oldest = time;
3965 			best   = pingptr;
3966 		}
3967 	}
3968 
3969 	return (best);
3970 }
3971 
3972 /*
3973 ==================
3974 CL_Ping_f
3975 ==================
3976 */
CL_Ping_f(void)3977 void CL_Ping_f( void ) {
3978 	netadr_t	to;
3979 	ping_t*		pingptr;
3980 	char*		server;
3981 	int			argc;
3982 	netadrtype_t	family = NA_UNSPEC;
3983 
3984 	argc = Cmd_Argc();
3985 
3986 	if ( argc != 2 && argc != 3 ) {
3987 		Com_Printf( "usage: ping [-4|-6] server\n");
3988 		return;
3989 	}
3990 
3991 	if(argc == 2)
3992 		server = Cmd_Argv(1);
3993 	else
3994 	{
3995 		if(!strcmp(Cmd_Argv(1), "-4"))
3996 			family = NA_IP;
3997 		else if(!strcmp(Cmd_Argv(1), "-6"))
3998 			family = NA_IP6;
3999 		else
4000 			Com_Printf( "warning: only -4 or -6 as address type understood.\n");
4001 
4002 		server = Cmd_Argv(2);
4003 	}
4004 
4005 	Com_Memset( &to, 0, sizeof(netadr_t) );
4006 
4007 	if ( !NET_StringToAdr( server, &to, family ) ) {
4008 		return;
4009 	}
4010 
4011 	pingptr = CL_GetFreePing();
4012 
4013 	memcpy( &pingptr->adr, &to, sizeof (netadr_t) );
4014 	pingptr->start = Sys_Milliseconds();
4015 	pingptr->time  = 0;
4016 
4017 	CL_SetServerInfoByAddress(pingptr->adr, NULL, 0);
4018 
4019 	NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" );
4020 }
4021 
4022 /*
4023 ==================
4024 CL_UpdateVisiblePings_f
4025 ==================
4026 */
CL_UpdateVisiblePings_f(int source)4027 qboolean CL_UpdateVisiblePings_f(int source) {
4028 	int			slots, i;
4029 	char		buff[MAX_STRING_CHARS];
4030 	int			pingTime;
4031 	int			max;
4032 	qboolean status = qfalse;
4033 
4034 	if (source < 0 || source > AS_FAVORITES) {
4035 		return qfalse;
4036 	}
4037 
4038 	cls.pingUpdateSource = source;
4039 
4040 	slots = CL_GetPingQueueCount();
4041 	if (slots < MAX_PINGREQUESTS) {
4042 		serverInfo_t *server = NULL;
4043 
4044 		max = (source == AS_GLOBAL) ? MAX_GLOBAL_SERVERS : MAX_OTHER_SERVERS;
4045 		switch (source) {
4046 			case AS_LOCAL :
4047 				server = &cls.localServers[0];
4048 				max = cls.numlocalservers;
4049 			break;
4050 			case AS_GLOBAL :
4051 				server = &cls.globalServers[0];
4052 				max = cls.numglobalservers;
4053 			break;
4054 			case AS_FAVORITES :
4055 				server = &cls.favoriteServers[0];
4056 				max = cls.numfavoriteservers;
4057 			break;
4058 			default:
4059 				return qfalse;
4060 		}
4061 		for (i = 0; i < max; i++) {
4062 			if (server[i].visible) {
4063 				if (server[i].ping == -1) {
4064 					int j;
4065 
4066 					if (slots >= MAX_PINGREQUESTS) {
4067 						break;
4068 					}
4069 					for (j = 0; j < MAX_PINGREQUESTS; j++) {
4070 						if (!cl_pinglist[j].adr.port) {
4071 							continue;
4072 						}
4073 						if (NET_CompareAdr( cl_pinglist[j].adr, server[i].adr)) {
4074 							// already on the list
4075 							break;
4076 						}
4077 					}
4078 					if (j >= MAX_PINGREQUESTS) {
4079 						status = qtrue;
4080 						for (j = 0; j < MAX_PINGREQUESTS; j++) {
4081 							if (!cl_pinglist[j].adr.port) {
4082 								break;
4083 							}
4084 						}
4085 						memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t));
4086 						cl_pinglist[j].start = Sys_Milliseconds();
4087 						cl_pinglist[j].time = 0;
4088 						NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" );
4089 						slots++;
4090 					}
4091 				}
4092 				// if the server has a ping higher than cl_maxPing or
4093 				// the ping packet got lost
4094 				else if (server[i].ping == 0) {
4095 					// if we are updating global servers
4096 					if (source == AS_GLOBAL) {
4097 						//
4098 						if ( cls.numGlobalServerAddresses > 0 ) {
4099 							// overwrite this server with one from the additional global servers
4100 							cls.numGlobalServerAddresses--;
4101 							CL_InitServerInfo(&server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses]);
4102 							// NOTE: the server[i].visible flag stays untouched
4103 						}
4104 					}
4105 				}
4106 			}
4107 		}
4108 	}
4109 
4110 	if (slots) {
4111 		status = qtrue;
4112 	}
4113 	for (i = 0; i < MAX_PINGREQUESTS; i++) {
4114 		if (!cl_pinglist[i].adr.port) {
4115 			continue;
4116 		}
4117 		CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime );
4118 		if (pingTime != 0) {
4119 			CL_ClearPing(i);
4120 			status = qtrue;
4121 		}
4122 	}
4123 
4124 	return status;
4125 }
4126 
4127 /*
4128 ==================
4129 CL_ServerStatus_f
4130 ==================
4131 */
CL_ServerStatus_f(void)4132 void CL_ServerStatus_f(void) {
4133 	netadr_t	to, *toptr = NULL;
4134 	char		*server;
4135 	serverStatus_t *serverStatus;
4136 	int			argc;
4137 	netadrtype_t	family = NA_UNSPEC;
4138 
4139 	argc = Cmd_Argc();
4140 
4141 	if ( argc != 2 && argc != 3 )
4142 	{
4143 		if (cls.state != CA_ACTIVE || clc.demoplaying)
4144 		{
4145 			Com_Printf ("Not connected to a server.\n");
4146 			Com_Printf( "usage: serverstatus [-4|-6] server\n");
4147 			return;
4148 		}
4149 
4150 		toptr = &clc.serverAddress;
4151 	}
4152 
4153 	if(!toptr)
4154 	{
4155 		Com_Memset( &to, 0, sizeof(netadr_t) );
4156 
4157 		if(argc == 2)
4158 			server = Cmd_Argv(1);
4159 		else
4160 		{
4161 			if(!strcmp(Cmd_Argv(1), "-4"))
4162 				family = NA_IP;
4163 			else if(!strcmp(Cmd_Argv(1), "-6"))
4164 				family = NA_IP6;
4165 			else
4166 				Com_Printf( "warning: only -4 or -6 as address type understood.\n");
4167 
4168 			server = Cmd_Argv(2);
4169 		}
4170 
4171 		toptr = &to;
4172 		if ( !NET_StringToAdr( server, toptr, family ) )
4173 			return;
4174 	}
4175 
4176 	NET_OutOfBandPrint( NS_CLIENT, *toptr, "getstatus" );
4177 
4178 	serverStatus = CL_GetServerStatus( *toptr );
4179 	serverStatus->address = *toptr;
4180 	serverStatus->print = qtrue;
4181 	serverStatus->pending = qtrue;
4182 }
4183 
4184 /*
4185 ==================
4186 CL_ShowIP_f
4187 ==================
4188 */
CL_ShowIP_f(void)4189 void CL_ShowIP_f(void) {
4190 	Sys_ShowIP();
4191 }
4192 
4193 #ifndef STANDALONE
4194 /*
4195 =================
4196 bool CL_CDKeyValidate
4197 =================
4198 */
CL_CDKeyValidate(const char * key,const char * checksum)4199 qboolean CL_CDKeyValidate( const char *key, const char *checksum ) {
4200 	return qtrue;
4201 }
4202 #endif
4203