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