1 /*
2 ===========================================================================
3 Copyright (C) 1999 - 2005, Id Software, Inc.
4 Copyright (C) 2000 - 2013, Raven Software, Inc.
5 Copyright (C) 2001 - 2013, Activision, Inc.
6 Copyright (C) 2005 - 2015, ioquake3 contributors
7 Copyright (C) 2013 - 2015, OpenJK contributors
8 
9 This file is part of the OpenJK source code.
10 
11 OpenJK is free software; you can redistribute it and/or modify it
12 under the terms of the GNU General Public License version 2 as
13 published by the Free Software Foundation.
14 
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 GNU General Public License for more details.
19 
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
22 ===========================================================================
23 */
24 
25 // cl_main.c  -- client main loop
26 
27 #include "client.h"
28 
29 #include <limits.h>
30 #include "ghoul2/G2.h"
31 #include "qcommon/cm_public.h"
32 #include "qcommon/MiniHeap.h"
33 #include "qcommon/stringed_ingame.h"
34 #include "cl_cgameapi.h"
35 #include "cl_uiapi.h"
36 #include "cl_lan.h"
37 #include "snd_local.h"
38 #include "sys/sys_loadlib.h"
39 
40 cvar_t	*cl_renderer;
41 
42 cvar_t	*cl_nodelta;
43 cvar_t	*cl_debugMove;
44 
45 cvar_t	*cl_noprint;
46 cvar_t	*cl_motd;
47 cvar_t	*cl_motdServer[MAX_MASTER_SERVERS];
48 
49 cvar_t	*rcon_client_password;
50 cvar_t	*rconAddress;
51 
52 cvar_t	*cl_timeout;
53 cvar_t	*cl_maxpackets;
54 cvar_t	*cl_packetdup;
55 cvar_t	*cl_timeNudge;
56 cvar_t	*cl_showTimeDelta;
57 cvar_t	*cl_freezeDemo;
58 
59 cvar_t	*cl_shownet;
60 cvar_t	*cl_showSend;
61 cvar_t	*cl_timedemo;
62 cvar_t	*cl_aviFrameRate;
63 cvar_t	*cl_aviMotionJpeg;
64 cvar_t	*cl_avi2GBLimit;
65 cvar_t	*cl_forceavidemo;
66 
67 cvar_t	*cl_freelook;
68 cvar_t	*cl_sensitivity;
69 
70 cvar_t	*cl_mouseAccel;
71 cvar_t	*cl_mouseAccelOffset;
72 cvar_t	*cl_mouseAccelStyle;
73 cvar_t	*cl_showMouseRate;
74 
75 cvar_t	*m_pitchVeh;
76 cvar_t	*m_pitch;
77 cvar_t	*m_yaw;
78 cvar_t	*m_forward;
79 cvar_t	*m_side;
80 cvar_t	*m_filter;
81 
82 cvar_t	*cl_activeAction;
83 
84 cvar_t	*cl_motdString;
85 
86 cvar_t	*cl_allowDownload;
87 cvar_t	*cl_allowAltEnter;
88 cvar_t	*cl_conXOffset;
89 cvar_t	*cl_inGameVideo;
90 
91 cvar_t	*cl_serverStatusResendTime;
92 cvar_t	*cl_framerate;
93 
94 // cvar to enable sending a "ja_guid" player identifier in userinfo to servers
95 // ja_guid is a persistent "cookie" that allows servers to track players across game sessions
96 cvar_t	*cl_enableGuid;
97 cvar_t	*cl_guidServerUniq;
98 
99 cvar_t	*cl_autolodscale;
100 
101 cvar_t	*cl_consoleKeys;
102 cvar_t	*cl_consoleUseScanCode;
103 
104 cvar_t  *cl_lanForcePackets;
105 
106 cvar_t	*cl_drawRecording;
107 
108 vec3_t cl_windVec;
109 
110 
111 clientActive_t		cl;
112 clientConnection_t	clc;
113 clientStatic_t		cls;
114 
115 netadr_t rcon_address;
116 
117 char cl_reconnectArgs[MAX_OSPATH] = {0};
118 
119 // Structure containing functions exported from refresh DLL
120 refexport_t	*re = NULL;
121 static void	*rendererLib = NULL;
122 
123 ping_t	cl_pinglist[MAX_PINGREQUESTS];
124 
125 typedef struct serverStatus_s
126 {
127 	char string[BIG_INFO_STRING];
128 	netadr_t address;
129 	int time, startTime;
130 	qboolean pending;
131 	qboolean print;
132 	qboolean retrieved;
133 } serverStatus_t;
134 
135 serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS];
136 int serverStatusCount;
137 
138 IHeapAllocator *G2VertSpaceClient = 0;
139 
140 extern void SV_BotFrame( int time );
141 void CL_CheckForResend( void );
142 void CL_ShowIP_f(void);
143 void CL_ServerStatus_f(void);
144 void CL_ServerStatusResponse( netadr_t from, msg_t *msg );
145 static void CL_ShutdownRef( qboolean restarting );
146 
147 /*
148 =======================================================================
149 
150 CLIENT RELIABLE COMMAND COMMUNICATION
151 
152 =======================================================================
153 */
154 
155 /*
156 ======================
157 CL_AddReliableCommand
158 
159 The given command will be transmitted to the server, and is gauranteed to
160 not have future usercmd_t executed before it is executed
161 ======================
162 */
CL_AddReliableCommand(const char * cmd,qboolean isDisconnectCmd)163 void CL_AddReliableCommand( const char *cmd, qboolean isDisconnectCmd ) {
164 	int unacknowledged = clc.reliableSequence - clc.reliableAcknowledge;
165 
166 	// if we would be losing an old command that hasn't been acknowledged,
167 	// we must drop the connection
168 	// also leave one slot open for the disconnect command in this case.
169 
170 	if ((isDisconnectCmd && unacknowledged > MAX_RELIABLE_COMMANDS) ||
171 	    (!isDisconnectCmd && unacknowledged >= MAX_RELIABLE_COMMANDS))
172 	{
173 		if(com_errorEntered)
174 			return;
175 		else
176 			Com_Error(ERR_DROP, "Client command overflow");
177 	}
178 
179 	Q_strncpyz(clc.reliableCommands[++clc.reliableSequence & (MAX_RELIABLE_COMMANDS - 1)],
180 		   cmd, sizeof(*clc.reliableCommands));
181 }
182 
183 /*
184 =======================================================================
185 
186 CLIENT SIDE DEMO RECORDING
187 
188 =======================================================================
189 */
190 
191 /*
192 ====================
193 CL_WriteDemoMessage
194 
195 Dumps the current net message, prefixed by the length
196 ====================
197 */
CL_WriteDemoMessage(msg_t * msg,int headerBytes)198 void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) {
199 	int		len, swlen;
200 
201 	// write the packet sequence
202 	len = clc.serverMessageSequence;
203 	swlen = LittleLong( len );
204 	FS_Write (&swlen, 4, clc.demofile);
205 
206 	// skip the packet sequencing information
207 	len = msg->cursize - headerBytes;
208 	swlen = LittleLong(len);
209 	FS_Write (&swlen, 4, clc.demofile);
210 	FS_Write ( msg->data + headerBytes, len, clc.demofile );
211 }
212 
213 
214 /*
215 ====================
216 CL_StopRecording_f
217 
218 stop recording a demo
219 ====================
220 */
CL_StopRecord_f(void)221 void CL_StopRecord_f( void ) {
222 	int		len;
223 
224 	if ( !clc.demorecording ) {
225 		Com_Printf ("Not recording a demo.\n");
226 		return;
227 	}
228 
229 	// finish up
230 	len = -1;
231 	FS_Write (&len, 4, clc.demofile);
232 	FS_Write (&len, 4, clc.demofile);
233 	FS_FCloseFile (clc.demofile);
234 	clc.demofile = 0;
235 	clc.demorecording = qfalse;
236 	clc.spDemoRecording = qfalse;
237 	Com_Printf ("Stopped demo.\n");
238 }
239 
240 /*
241 ==================
242 CL_DemoFilename
243 ==================
244 */
CL_DemoFilename(char * buf,int bufSize)245 void CL_DemoFilename( char *buf, int bufSize ) {
246 	time_t rawtime;
247 	char timeStr[32] = {0}; // should really only reach ~19 chars
248 
249 	time( &rawtime );
250 	strftime( timeStr, sizeof( timeStr ), "%Y-%m-%d_%H-%M-%S", localtime( &rawtime ) ); // or gmtime
251 
252 	Com_sprintf( buf, bufSize, "demo%s", timeStr );
253 }
254 
255 /*
256 ====================
257 CL_Record_f
258 
259 record <demoname>
260 
261 Begins recording a demo from the current position
262 ====================
263 */
264 static char		demoName[MAX_QPATH];	// compiler bug workaround
CL_Record_f(void)265 void CL_Record_f( void ) {
266 	char		name[MAX_OSPATH];
267 	byte		bufData[MAX_MSGLEN];
268 	msg_t	buf;
269 	int			i;
270 	int			len;
271 	entityState_t	*ent;
272 	entityState_t	nullstate;
273 	char		*s;
274 
275 	if ( Cmd_Argc() > 2 ) {
276 		Com_Printf ("record <demoname>\n");
277 		return;
278 	}
279 
280 	if ( clc.demorecording ) {
281 		if (!clc.spDemoRecording) {
282 			Com_Printf ("Already recording.\n");
283 		}
284 		return;
285 	}
286 
287 	if ( cls.state != CA_ACTIVE ) {
288 		Com_Printf ("You must be in a level to record.\n");
289 		return;
290 	}
291 
292 	// sync 0 doesn't prevent recording, so not forcing it off .. everyone does g_sync 1 ; record ; g_sync 0 ..
293 	if ( NET_IsLocalAddress( clc.serverAddress ) && !Cvar_VariableValue( "g_synchronousClients" ) ) {
294 		Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n");
295 	}
296 
297 	if ( Cmd_Argc() == 2 ) {
298 		s = Cmd_Argv(1);
299 		Q_strncpyz( demoName, s, sizeof( demoName ) );
300 		Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION );
301 	} else {
302 		// timestamp the file
303 		CL_DemoFilename( demoName, sizeof( demoName ) );
304 
305 		Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION );
306 
307 		if ( FS_FileExists( name ) ) {
308 			Com_Printf( "Record: Couldn't create a file\n");
309 			return;
310  		}
311 	}
312 
313 	// open the demo file
314 
315 	Com_Printf ("recording to %s.\n", name);
316 	clc.demofile = FS_FOpenFileWrite( name );
317 	if ( !clc.demofile ) {
318 		Com_Printf ("ERROR: couldn't open.\n");
319 		return;
320 	}
321 	clc.demorecording = qtrue;
322 	if (Cvar_VariableValue("ui_recordSPDemo")) {
323 	  clc.spDemoRecording = qtrue;
324 	} else {
325 	  clc.spDemoRecording = qfalse;
326 	}
327 
328 	Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) );
329 
330 	// don't start saving messages until a non-delta compressed message is received
331 	clc.demowaiting = qtrue;
332 
333 	// write out the gamestate message
334 	MSG_Init (&buf, bufData, sizeof(bufData));
335 	MSG_Bitstream(&buf);
336 
337 	// NOTE, MRE: all server->client messages now acknowledge
338 	MSG_WriteLong( &buf, clc.reliableSequence );
339 
340 	MSG_WriteByte (&buf, svc_gamestate);
341 	MSG_WriteLong (&buf, clc.serverCommandSequence );
342 
343 	// configstrings
344 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
345 		if ( !cl.gameState.stringOffsets[i] ) {
346 			continue;
347 		}
348 		s = cl.gameState.stringData + cl.gameState.stringOffsets[i];
349 		MSG_WriteByte (&buf, svc_configstring);
350 		MSG_WriteShort (&buf, i);
351 		MSG_WriteBigString (&buf, s);
352 	}
353 
354 	// baselines
355 	Com_Memset (&nullstate, 0, sizeof(nullstate));
356 	for ( i = 0; i < MAX_GENTITIES ; i++ ) {
357 		ent = &cl.entityBaselines[i];
358 		if ( !ent->number ) {
359 			continue;
360 		}
361 		MSG_WriteByte (&buf, svc_baseline);
362 		MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue );
363 	}
364 
365 	MSG_WriteByte( &buf, svc_EOF );
366 
367 	// finished writing the gamestate stuff
368 
369 	// write the client num
370 	MSG_WriteLong(&buf, clc.clientNum);
371 	// write the checksum feed
372 	MSG_WriteLong(&buf, clc.checksumFeed);
373 
374 	// Filler for old RMG system.
375 	MSG_WriteShort ( &buf, 0 );
376 
377 	// finished writing the client packet
378 	MSG_WriteByte( &buf, svc_EOF );
379 
380 	// write it to the demo file
381 	len = LittleLong( clc.serverMessageSequence - 1 );
382 	FS_Write (&len, 4, clc.demofile);
383 
384 	len = LittleLong (buf.cursize);
385 	FS_Write (&len, 4, clc.demofile);
386 	FS_Write (buf.data, buf.cursize, clc.demofile);
387 
388 	// the rest of the demo file will be copied from net messages
389 }
390 
391 /*
392 =======================================================================
393 
394 CLIENT SIDE DEMO PLAYBACK
395 
396 =======================================================================
397 */
398 
399 /*
400 =================
401 CL_DemoCompleted
402 =================
403 */
CL_DemoCompleted(void)404 void CL_DemoCompleted( void ) {
405 	if (cl_timedemo && cl_timedemo->integer) {
406 		int	time;
407 
408 		time = Sys_Milliseconds() - clc.timeDemoStart;
409 		if ( time > 0 ) {
410 			Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", clc.timeDemoFrames,
411 			time/1000.0, clc.timeDemoFrames*1000.0 / time);
412 		}
413 	}
414 
415 /*	CL_Disconnect( qtrue );
416 	CL_NextDemo();
417 	*/
418 
419 	//rww - The above code seems to just stick you in a no-menu state and you can't do anything there.
420 	//I'm not sure why it ever worked in TA, but whatever. This code will bring us back to the main menu
421 	//after a demo is finished playing instead.
422 	CL_Disconnect_f();
423 	S_StopAllSounds();
424 	UIVM_SetActiveMenu( UIMENU_MAIN );
425 
426 	CL_NextDemo();
427 }
428 
429 /*
430 =================
431 CL_ReadDemoMessage
432 =================
433 */
CL_ReadDemoMessage(void)434 void CL_ReadDemoMessage( void ) {
435 	int			r;
436 	msg_t		buf;
437 	byte		bufData[ MAX_MSGLEN ];
438 	int			s;
439 
440 	if ( !clc.demofile ) {
441 		CL_DemoCompleted ();
442 		return;
443 	}
444 
445 	// get the sequence number
446 	r = FS_Read( &s, 4, clc.demofile);
447 	if ( r != 4 ) {
448 		CL_DemoCompleted ();
449 		return;
450 	}
451 	clc.serverMessageSequence = LittleLong( s );
452 
453 	// init the message
454 	MSG_Init( &buf, bufData, sizeof( bufData ) );
455 
456 	// get the length
457 	r = FS_Read (&buf.cursize, 4, clc.demofile);
458 	if ( r != 4 ) {
459 		CL_DemoCompleted ();
460 		return;
461 	}
462 	buf.cursize = LittleLong( buf.cursize );
463 	if ( buf.cursize == -1 ) {
464 		CL_DemoCompleted ();
465 		return;
466 	}
467 	if ( buf.cursize > buf.maxsize ) {
468 		Com_Error (ERR_DROP, "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN");
469 	}
470 	r = FS_Read( buf.data, buf.cursize, clc.demofile );
471 	if ( r != buf.cursize ) {
472 		Com_Printf( "Demo file was truncated.\n");
473 		CL_DemoCompleted ();
474 		return;
475 	}
476 
477 	clc.lastPacketTime = cls.realtime;
478 	buf.readcount = 0;
479 	CL_ParseServerMessage( &buf );
480 }
481 
482 /*
483 ====================
484 CL_CompleteDemoName
485 ====================
486 */
CL_CompleteDemoName(char * args,int argNum)487 static void CL_CompleteDemoName( char *args, int argNum )
488 {
489 	if( argNum == 2 )
490 	{
491 		char demoExt[16];
492 
493 		Com_sprintf(demoExt, sizeof(demoExt), ".dm_%d", PROTOCOL_VERSION);
494 		Field_CompleteFilename( "demos", demoExt, qtrue, qtrue );
495 	}
496 }
497 
498 /*
499 ====================
500 CL_PlayDemo_f
501 
502 demo <demoname>
503 
504 ====================
505 */
CL_PlayDemo_f(void)506 void CL_PlayDemo_f( void ) {
507 	char		name[MAX_OSPATH], extension[32];
508 	char		*arg;
509 
510 	if (Cmd_Argc() != 2) {
511 		Com_Printf ("demo <demoname>\n");
512 		return;
513 	}
514 
515 	// make sure a local server is killed
516 	// 2 means don't force disconnect of local client
517 	Cvar_Set( "sv_killserver", "2" );
518 
519 	// open the demo file
520 	arg = Cmd_Argv(1);
521 
522 	CL_Disconnect( qtrue );
523 
524 	Com_sprintf(extension, sizeof(extension), ".dm_%d", PROTOCOL_VERSION);
525 	if ( !Q_stricmp( arg + strlen(arg) - strlen(extension), extension ) ) {
526 		Com_sprintf (name, sizeof(name), "demos/%s", arg);
527 	} else {
528 		Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", arg, PROTOCOL_VERSION);
529 	}
530 
531 	FS_FOpenFileRead( name, &clc.demofile, qtrue );
532 	if (!clc.demofile) {
533 		if (!Q_stricmp(arg, "(null)"))
534 		{
535 			Com_Error( ERR_DROP, SE_GetString("CON_TEXT_NO_DEMO_SELECTED") );
536 		}
537 		else
538 		{
539 			Com_Error( ERR_DROP, "couldn't open %s", name);
540 		}
541 		return;
542 	}
543 	Q_strncpyz( clc.demoName, Cmd_Argv(1), sizeof( clc.demoName ) );
544 
545 	Con_Close();
546 
547 	cls.state = CA_CONNECTED;
548 	clc.demoplaying = qtrue;
549 	Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) );
550 
551 	// read demo messages until connected
552 	while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) {
553 		CL_ReadDemoMessage();
554 	}
555 	// don't get the first snapshot this frame, to prevent the long
556 	// time from the gamestate load from messing causing a time skip
557 	clc.firstDemoFrameSkipped = qfalse;
558 }
559 
560 
561 /*
562 ====================
563 CL_StartDemoLoop
564 
565 Closing the main menu will restart the demo loop
566 ====================
567 */
CL_StartDemoLoop(void)568 void CL_StartDemoLoop( void ) {
569 	// start the demo loop again
570 	Cbuf_AddText ("d1\n");
571 	Key_SetCatcher( 0 );
572 }
573 
574 /*
575 ==================
576 CL_NextDemo
577 
578 Called when a demo or cinematic finishes
579 If the "nextdemo" cvar is set, that command will be issued
580 ==================
581 */
CL_NextDemo(void)582 void CL_NextDemo( void ) {
583 	char	v[MAX_STRING_CHARS];
584 
585 	Q_strncpyz( v, Cvar_VariableString ("nextdemo"), sizeof(v) );
586 	v[MAX_STRING_CHARS-1] = 0;
587 	Com_DPrintf("CL_NextDemo: %s\n", v );
588 	if (!v[0]) {
589 		return;
590 	}
591 
592 	Cvar_Set ("nextdemo","");
593 	Cbuf_AddText (v);
594 	Cbuf_AddText ("\n");
595 	Cbuf_Execute();
596 }
597 
598 //======================================================================
599 
600 /*
601 =====================
602 CL_ShutdownAll
603 =====================
604 */
CL_ShutdownAll(qboolean shutdownRef)605 void CL_ShutdownAll( qboolean shutdownRef ) {
606 	if(CL_VideoRecording())
607 		CL_CloseAVI();
608 
609 	if(clc.demorecording)
610 		CL_StopRecord_f();
611 
612 #if 0 //rwwFIXMEFIXME: Disable this before release!!!!!! I am just trying to find a crash bug.
613 	//so it doesn't barf on shutdown saying refentities belong to each other
614 	tr.refdef.num_entities = 0;
615 #endif
616 
617 	// clear sounds
618 	S_DisableSounds();
619 	// shutdown CGame
620 	CL_ShutdownCGame();
621 	// shutdown UI
622 	CL_ShutdownUI();
623 
624 	// shutdown the renderer
625 	if(shutdownRef)
626 		CL_ShutdownRef( qfalse );
627 	if ( re && re->Shutdown ) {
628 		re->Shutdown( qfalse, qfalse );		// don't destroy window or context
629 	}
630 
631 	cls.uiStarted = qfalse;
632 	cls.cgameStarted = qfalse;
633 	cls.rendererStarted = qfalse;
634 	cls.soundRegistered = qfalse;
635 }
636 
637 /*
638 =================
639 CL_FlushMemory
640 
641 Called by CL_MapLoading, CL_Connect_f, CL_PlayDemo_f, and CL_ParseGamestate the only
642 ways a client gets into a game
643 Also called by Com_Error
644 =================
645 */
CL_FlushMemory(void)646 void CL_FlushMemory( void ) {
647 
648 	// shutdown all the client stuff
649 	CL_ShutdownAll( qfalse );
650 
651 	// if not running a server clear the whole hunk
652 	if ( !com_sv_running->integer ) {
653 		// clear collision map data
654 		CM_ClearMap();
655 		// clear the whole hunk
656 		Hunk_Clear();
657 	}
658 	else {
659 		// clear all the client data on the hunk
660 		Hunk_ClearToMark();
661 	}
662 
663 	CL_StartHunkUsers();
664 }
665 
666 /*
667 =====================
668 CL_MapLoading
669 
670 A local server is starting to load a map, so update the
671 screen to let the user know about it, then dump all client
672 memory on the hunk from cgame, ui, and renderer
673 =====================
674 */
CL_MapLoading(void)675 void CL_MapLoading( void ) {
676 	if ( !com_cl_running->integer ) {
677 		return;
678 	}
679 
680 	// Set this to localhost.
681 	Cvar_Set( "cl_currentServerAddress", "Localhost");
682 	Cvar_Set( "cl_currentServerIP", "loopback");
683 
684 	Con_Close();
685 	Key_SetCatcher( 0 );
686 
687 	// if we are already connected to the local host, stay connected
688 	if ( cls.state >= CA_CONNECTED && !Q_stricmp( cls.servername, "localhost" ) ) {
689 		cls.state = CA_CONNECTED;		// so the connect screen is drawn
690 		Com_Memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) );
691 		Com_Memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) );
692 		Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) );
693 		clc.lastPacketSentTime = -9999;
694 		SCR_UpdateScreen();
695 	} else {
696 		// clear nextmap so the cinematic shutdown doesn't execute it
697 		Cvar_Set( "nextmap", "" );
698 		CL_Disconnect( qtrue );
699 		Q_strncpyz( cls.servername, "localhost", sizeof(cls.servername) );
700 		cls.state = CA_CHALLENGING;		// so the connect screen is drawn
701 		Key_SetCatcher( 0 );
702 		SCR_UpdateScreen();
703 		clc.connectTime = -RETRANSMIT_TIMEOUT;
704 		NET_StringToAdr( cls.servername, &clc.serverAddress);
705 		// we don't need a challenge on the localhost
706 
707 		CL_CheckForResend();
708 	}
709 }
710 
711 /*
712 =====================
713 CL_ClearState
714 
715 Called before parsing a gamestate
716 =====================
717 */
CL_ClearState(void)718 void CL_ClearState (void) {
719 
720 //	S_StopAllSounds();
721 	Com_Memset( &cl, 0, sizeof( cl ) );
722 }
723 
724 /*
725 ====================
726 CL_UpdateGUID
727 
728 update cl_guid using QKEY_FILE and optional prefix
729 ====================
730 */
CL_UpdateGUID(const char * prefix,int prefix_len)731 static void CL_UpdateGUID( const char *prefix, int prefix_len )
732 {
733 	if (cl_enableGuid->integer) {
734 		fileHandle_t f;
735 		int len;
736 
737 		len = FS_SV_FOpenFileRead( QKEY_FILE, &f );
738 		FS_FCloseFile( f );
739 
740 		// initialize the cvar here in case it's unset or was user-created
741 		// while tracking was disabled (removes CVAR_USER_CREATED)
742 		Cvar_Get( "ja_guid", "", CVAR_USERINFO | CVAR_ROM, "Client GUID" );
743 
744 		if( len != QKEY_SIZE ) {
745 			Cvar_Set( "ja_guid", "" );
746 		} else {
747 			Cvar_Set( "ja_guid", Com_MD5File( QKEY_FILE, QKEY_SIZE,
748 				prefix, prefix_len ) );
749 		}
750 	} else {
751 		// Remove the cvar entirely if tracking is disabled
752 		uint32_t flags = Cvar_Flags("ja_guid");
753 		// keep the cvar if it's user-created, but destroy it otherwise
754 		if (flags != CVAR_NONEXISTENT && !(flags & CVAR_USER_CREATED)) {
755 			cvar_t *ja_guid = Cvar_Get("ja_guid", "", 0, "Client GUID" );
756 			Cvar_Unset(ja_guid);
757 		}
758 	}
759 }
760 
761 /*
762 =====================
763 CL_Disconnect
764 
765 Called when a connection, demo, or cinematic is being terminated.
766 Goes from a connected state to either a menu state or a console state
767 Sends a disconnect message to the server
768 This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors
769 =====================
770 */
CL_Disconnect(qboolean showMainMenu)771 void CL_Disconnect( qboolean showMainMenu ) {
772 	if ( !com_cl_running || !com_cl_running->integer ) {
773 		return;
774 	}
775 
776 	// shutting down the client so enter full screen ui mode
777 	Cvar_Set("r_uiFullScreen", "1");
778 
779 	if ( clc.demorecording ) {
780 		CL_StopRecord_f ();
781 	}
782 
783 	if (clc.download) {
784 		FS_FCloseFile( clc.download );
785 		clc.download = 0;
786 	}
787 	*clc.downloadTempName = *clc.downloadName = 0;
788 	Cvar_Set( "cl_downloadName", "" );
789 
790 	if ( clc.demofile ) {
791 		FS_FCloseFile( clc.demofile );
792 		clc.demofile = 0;
793 	}
794 
795 	if ( cls.uiStarted && showMainMenu ) {
796 		UIVM_SetActiveMenu( UIMENU_NONE );
797 	}
798 
799 	SCR_StopCinematic ();
800 	S_ClearSoundBuffer();
801 
802 	// send a disconnect message to the server
803 	// send it a few times in case one is dropped
804 	if ( cls.state >= CA_CONNECTED ) {
805 		CL_AddReliableCommand( "disconnect", qtrue );
806 		CL_WritePacket();
807 		CL_WritePacket();
808 		CL_WritePacket();
809 	}
810 
811 	// Remove pure paks
812 	FS_PureServerSetLoadedPaks("", "");
813 	FS_PureServerSetReferencedPaks("", "");
814 
815 	CL_ClearState ();
816 
817 	// wipe the client connection
818 	Com_Memset( &clc, 0, sizeof( clc ) );
819 
820 	cls.state = CA_DISCONNECTED;
821 
822 	// allow cheats locally
823 	Cvar_Set( "sv_cheats", "1" );
824 
825 	// not connected to a pure server anymore
826 	cl_connectedToPureServer = qfalse;
827 
828 	// Stop recording any video
829 	if( CL_VideoRecording( ) ) {
830 		// Finish rendering current frame
831 		SCR_UpdateScreen( );
832 		CL_CloseAVI( );
833 	}
834 
835 	CL_UpdateGUID( NULL, 0 );
836 }
837 
838 
839 /*
840 ===================
841 CL_ForwardCommandToServer
842 
843 adds the current command line as a clientCommand
844 things like godmode, noclip, etc, are commands directed to the server,
845 so when they are typed in at the console, they will need to be forwarded.
846 ===================
847 */
CL_ForwardCommandToServer(const char * string)848 void CL_ForwardCommandToServer( const char *string ) {
849 	char	*cmd;
850 
851 	cmd = Cmd_Argv(0);
852 
853 	// ignore key up commands
854 	if ( cmd[0] == '-' ) {
855 		return;
856 	}
857 
858 	if (clc.demoplaying || cls.state < CA_CONNECTED || cmd[0] == '+' ) {
859 		Com_Printf ("Unknown command \"%s" S_COLOR_WHITE "\"\n", cmd);
860 		return;
861 	}
862 
863 	if ( Cmd_Argc() > 1 ) {
864 		CL_AddReliableCommand( string, qfalse );
865 	} else {
866 		CL_AddReliableCommand( cmd, qfalse );
867 	}
868 }
869 
870 /*
871 ===================
872 CL_RequestMotd
873 
874 ===================
875 */
CL_RequestMotd(void)876 void CL_RequestMotd( void ) {
877 	netadr_t	to;
878 	int			i;
879 	char		command[MAX_STRING_CHARS], info[MAX_INFO_STRING];
880 	char		*motdaddress;
881 
882 	if ( !cl_motd->integer ) {
883 		return;
884 	}
885 
886 	if ( cl_motd->integer < 1 || cl_motd->integer > MAX_MASTER_SERVERS ) {
887 		Com_Printf( "CL_RequestMotd: Invalid motd server num. Valid values are 1-%d or 0 to disable\n", MAX_MASTER_SERVERS );
888 		return;
889 	}
890 
891 	Com_sprintf( command, sizeof(command), "cl_motdServer%d", cl_motd->integer );
892 	motdaddress = Cvar_VariableString( command );
893 
894 	if ( !*motdaddress )
895 	{
896 		Com_Printf( "CL_RequestMotd: Error: No motd server address given.\n" );
897 		return;
898 	}
899 
900 	i = NET_StringToAdr( motdaddress, &to );
901 
902 	if ( !i )
903 	{
904 		Com_Printf( "CL_RequestMotd: Error: could not resolve address of motd server %s\n", motdaddress );
905 		return;
906 	}
907 	to.type = NA_IP;
908 	to.port = BigShort( PORT_UPDATE );
909 
910 	Com_Printf( "Requesting motd from update %s (%s)...\n", motdaddress, NET_AdrToString( to ) );
911 
912 	cls.updateServer = to;
913 
914 	info[0] = 0;
915   // NOTE TTimo xoring against Com_Milliseconds, otherwise we may not have a true randomization
916   // only srand I could catch before here is tr_noise.c l:26 srand(1001)
917   // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=382
918   // NOTE: the Com_Milliseconds xoring only affects the lower 16-bit word,
919   //   but I decided it was enough randomization
920 	Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds());
921 
922 	Info_SetValueForKey( info, "challenge", cls.updateChallenge );
923 	Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string );
924 	Info_SetValueForKey( info, "rvendor", cls.glconfig.vendor_string );
925 	Info_SetValueForKey( info, "version", com_version->string );
926 
927 	//If raven starts filtering for this, add this code back in
928 #if 0
929 	Info_SetValueForKey( info, "cputype", "Intel Pentium IV");
930 	Info_SetValueForKey( info, "mhz", "3000" );
931 	Info_SetValueForKey( info, "memory", "4096" );
932 #endif
933 	Info_SetValueForKey( info, "joystick", Cvar_VariableString("in_joystick") );
934 	Info_SetValueForKey( info, "colorbits", va("%d",cls.glconfig.colorBits) );
935 
936 	NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info );
937 }
938 
939 
940 /*
941 ======================================================================
942 
943 CONSOLE COMMANDS
944 
945 ======================================================================
946 */
947 
948 /*
949 ==================
950 CL_ForwardToServer_f
951 ==================
952 */
CL_ForwardToServer_f(void)953 void CL_ForwardToServer_f( void ) {
954 	if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
955 		Com_Printf ("Not connected to a server.\n");
956 		return;
957 	}
958 
959 	// don't forward the first argument
960 	if ( Cmd_Argc() > 1 ) {
961 		CL_AddReliableCommand( Cmd_Args(), qfalse );
962 	}
963 }
964 
965 
966 /*
967 ==================
968 CL_Disconnect_f
969 ==================
970 */
CL_Disconnect_f(void)971 void CL_Disconnect_f( void ) {
972 	SCR_StopCinematic();
973 	Cvar_Set("ui_singlePlayerActive", "0");
974 	if ( cls.state != CA_DISCONNECTED && cls.state != CA_CINEMATIC ) {
975 		Com_Error (ERR_DISCONNECT, "Disconnected from server");
976 	}
977 }
978 
979 
980 /*
981 ================
982 CL_Reconnect_f
983 
984 ================
985 */
CL_Reconnect_f(void)986 void CL_Reconnect_f( void ) {
987 	if ( !strlen( cl_reconnectArgs ) ) {
988 		return;
989 	}
990 	Cvar_Set("ui_singlePlayerActive", "0");
991 	Cbuf_AddText( va("connect %s\n", cl_reconnectArgs ) );
992 }
993 
994 /*
995 ================
996 CL_Connect_f
997 
998 ================
999 */
CL_Connect_f(void)1000 void CL_Connect_f( void ) {
1001 	char	*server;
1002 	const char	*serverString;
1003 
1004 	if ( Cmd_Argc() != 2 ) {
1005 		Com_Printf( "usage: connect [server]\n");
1006 		return;
1007 	}
1008 
1009 	// save arguments for reconnect
1010 	Q_strncpyz( cl_reconnectArgs, Cmd_Args(), sizeof( cl_reconnectArgs ) );
1011 
1012 	Cvar_Set("ui_singlePlayerActive", "0");
1013 
1014 	// fire a message off to the motd server
1015 	CL_RequestMotd();
1016 
1017 	// clear any previous "server full" type messages
1018 	clc.serverMessage[0] = 0;
1019 
1020 	server = Cmd_Argv (1);
1021 
1022 	if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) {
1023 		// if running a local server, kill it
1024 		SV_Shutdown( "Server quit\n" );
1025 	}
1026 
1027 	// make sure a local server is killed
1028 	Cvar_Set( "sv_killserver", "1" );
1029 	SV_Frame( 0 );
1030 
1031 	CL_Disconnect( qtrue );
1032 	Con_Close();
1033 
1034 	Q_strncpyz( cls.servername, server, sizeof(cls.servername) );
1035 
1036 	if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) {
1037 		Com_Printf ("Bad server address\n");
1038 		cls.state = CA_DISCONNECTED;
1039 		return;
1040 	}
1041 	if (clc.serverAddress.port == 0) {
1042 		clc.serverAddress.port = BigShort( PORT_SERVER );
1043 	}
1044 
1045 	serverString = NET_AdrToString(clc.serverAddress);
1046 
1047 	Com_Printf( "%s resolved to %s\n", cls.servername, serverString );
1048 
1049 	if( cl_guidServerUniq->integer )
1050 		CL_UpdateGUID( serverString, strlen( serverString ) );
1051 	else
1052 		CL_UpdateGUID( NULL, 0 );
1053 
1054 	// if we aren't playing on a lan, we need to authenticate
1055 	if ( NET_IsLocalAddress( clc.serverAddress ) ) {
1056 		cls.state = CA_CHALLENGING;
1057 	} else {
1058 		cls.state = CA_CONNECTING;
1059 
1060 		// Set a client challenge number that ideally is mirrored back by the server.
1061 		clc.challenge = ((rand() << 16) ^ rand()) ^ Com_Milliseconds();
1062 	}
1063 
1064 	Key_SetCatcher( 0 );
1065 	clc.connectTime = -99999;	// CL_CheckForResend() will fire immediately
1066 	clc.connectPacketCount = 0;
1067 
1068 	// server connection string
1069 	Cvar_Set( "cl_currentServerAddress", server );
1070 	Cvar_Set( "cl_currentServerIP", serverString );
1071 }
1072 
1073 #define MAX_RCON_MESSAGE 1024
1074 
1075 /*
1076 ==================
1077 CL_CompleteRcon
1078 ==================
1079 */
CL_CompleteRcon(char * args,int argNum)1080 static void CL_CompleteRcon( char *args, int argNum )
1081 {
1082 	if( argNum == 2 )
1083 	{
1084 		// Skip "rcon "
1085 		char *p = Com_SkipTokens( args, 1, " " );
1086 
1087 		if( p > args )
1088 			Field_CompleteCommand( p, qtrue, qtrue );
1089 	}
1090 }
1091 
1092 /*
1093 =====================
1094 CL_Rcon_f
1095 
1096   Send the rest of the command line over as
1097   an unconnected command.
1098 =====================
1099 */
CL_Rcon_f(void)1100 void CL_Rcon_f( void ) {
1101 	char	message[MAX_RCON_MESSAGE];
1102 
1103 	if ( !rcon_client_password->string[0] ) {
1104 		Com_Printf( "You must set 'rconpassword' before issuing an rcon command.\n" );
1105 		return;
1106 	}
1107 
1108 	message[0] = -1;
1109 	message[1] = -1;
1110 	message[2] = -1;
1111 	message[3] = -1;
1112 	message[4] = 0;
1113 
1114 	Q_strcat (message, MAX_RCON_MESSAGE, "rcon ");
1115 
1116 	Q_strcat (message, MAX_RCON_MESSAGE, rcon_client_password->string);
1117 	Q_strcat (message, MAX_RCON_MESSAGE, " ");
1118 
1119 	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
1120 	Q_strcat (message, MAX_RCON_MESSAGE, Cmd_Cmd()+5);
1121 
1122 	if ( cls.state >= CA_CONNECTED ) {
1123 		rcon_address = clc.netchan.remoteAddress;
1124 	} else {
1125 		if (!strlen(rconAddress->string)) {
1126 			Com_Printf ("You must either be connected,\n"
1127 						"or set the 'rconAddress' cvar\n"
1128 						"to issue rcon commands\n");
1129 
1130 			return;
1131 		}
1132 		NET_StringToAdr (rconAddress->string, &rcon_address);
1133 		if (rcon_address.port == 0) {
1134 			rcon_address.port = BigShort (PORT_SERVER);
1135 		}
1136 	}
1137 
1138 	NET_SendPacket (NS_CLIENT, strlen(message)+1, message, rcon_address);
1139 }
1140 
1141 /*
1142 =================
1143 CL_SendPureChecksums
1144 =================
1145 */
CL_SendPureChecksums(void)1146 void CL_SendPureChecksums( void ) {
1147 	char cMsg[MAX_INFO_VALUE];
1148 
1149 	// if we are pure we need to send back a command with our referenced pk3 checksums
1150 	Com_sprintf(cMsg, sizeof(cMsg), "cp %s", FS_ReferencedPakPureChecksums());
1151 
1152 	CL_AddReliableCommand( cMsg, qfalse );
1153 }
1154 
1155 /*
1156 =================
1157 CL_ResetPureClientAtServer
1158 =================
1159 */
CL_ResetPureClientAtServer(void)1160 void CL_ResetPureClientAtServer( void ) {
1161 	CL_AddReliableCommand( "vdr", qfalse );
1162 }
1163 
1164 /*
1165 =================
1166 CL_Vid_Restart_f
1167 
1168 Restart the video subsystem
1169 
1170 we also have to reload the UI and CGame because the renderer
1171 doesn't know what graphics to reload
1172 =================
1173 */
1174 extern bool g_nOverrideChecked;
CL_Vid_Restart_f(void)1175 void CL_Vid_Restart_f( void ) {
1176 	// Settings may have changed so stop recording now
1177 	if( CL_VideoRecording( ) ) {
1178 		CL_CloseAVI( );
1179 	}
1180 
1181 	if(clc.demorecording)
1182 		CL_StopRecord_f();
1183 
1184 	//rww - sort of nasty, but when a user selects a mod
1185 	//from the menu all it does is a vid_restart, so we
1186 	//have to check for new net overrides for the mod then.
1187 	g_nOverrideChecked = false;
1188 
1189 	// don't let them loop during the restart
1190 	S_StopAllSounds();
1191 	// shutdown the UI
1192 	CL_ShutdownUI();
1193 	// shutdown the CGame
1194 	CL_ShutdownCGame();
1195 	// shutdown the renderer and clear the renderer interface
1196 	CL_ShutdownRef( qtrue );
1197 	// client is no longer pure untill new checksums are sent
1198 	CL_ResetPureClientAtServer();
1199 	// clear pak references
1200 	FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF );
1201 	// reinitialize the filesystem if the game directory or checksum has changed
1202 	FS_ConditionalRestart( clc.checksumFeed );
1203 
1204 	cls.rendererStarted = qfalse;
1205 	cls.uiStarted = qfalse;
1206 	cls.cgameStarted = qfalse;
1207 	cls.soundRegistered = qfalse;
1208 
1209 	// unpause so the cgame definately gets a snapshot and renders a frame
1210 	Cvar_Set( "cl_paused", "0" );
1211 
1212 	// if not running a server clear the whole hunk
1213 	if ( !com_sv_running->integer ) {
1214 		CM_ClearMap();
1215 		// clear the whole hunk
1216 		Hunk_Clear();
1217 	}
1218 	else {
1219 		// clear all the client data on the hunk
1220 		Hunk_ClearToMark();
1221 	}
1222 
1223 	// initialize the renderer interface
1224 	CL_InitRef();
1225 
1226 	// startup all the client stuff
1227 	CL_StartHunkUsers();
1228 
1229 	// start the cgame if connected
1230 	if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) {
1231 		cls.cgameStarted = qtrue;
1232 		CL_InitCGame();
1233 		// send pure checksums
1234 		CL_SendPureChecksums();
1235 	}
1236 }
1237 
1238 /*
1239 =================
1240 CL_Snd_Restart_f
1241 
1242 Restart the sound subsystem
1243 The cgame and game must also be forced to restart because
1244 handles will be invalid
1245 =================
1246 */
1247 // extern void S_UnCacheDynamicMusic( void );
CL_Snd_Restart_f(void)1248 void CL_Snd_Restart_f( void ) {
1249 	S_Shutdown();
1250 	S_Init();
1251 
1252 //	S_FreeAllSFXMem();			// These two removed by BTO (VV)
1253 //	S_UnCacheDynamicMusic();	// S_Shutdown() already does this!
1254 
1255 //	CL_Vid_Restart_f();
1256 
1257 	extern qboolean	s_soundMuted;
1258 	s_soundMuted = qfalse;		// we can play again
1259 
1260 	extern void S_RestartMusic( void );
1261 	S_RestartMusic();
1262 }
1263 
1264 
1265 /*
1266 ==================
1267 CL_PK3List_f
1268 ==================
1269 */
CL_OpenedPK3List_f(void)1270 void CL_OpenedPK3List_f( void ) {
1271 	Com_Printf("Opened PK3 Names: %s\n", FS_LoadedPakNames());
1272 }
1273 
1274 /*
1275 ==================
1276 CL_PureList_f
1277 ==================
1278 */
CL_ReferencedPK3List_f(void)1279 void CL_ReferencedPK3List_f( void ) {
1280 	Com_Printf("Referenced PK3 Names: %s\n", FS_ReferencedPakNames());
1281 }
1282 
1283 /*
1284 ==================
1285 CL_Configstrings_f
1286 ==================
1287 */
CL_Configstrings_f(void)1288 void CL_Configstrings_f( void ) {
1289 	int		i;
1290 	int		ofs;
1291 
1292 	if ( cls.state != CA_ACTIVE ) {
1293 		Com_Printf( "Not connected to a server.\n");
1294 		return;
1295 	}
1296 
1297 	for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
1298 		ofs = cl.gameState.stringOffsets[ i ];
1299 		if ( !ofs ) {
1300 			continue;
1301 		}
1302 		Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs );
1303 	}
1304 }
1305 
1306 /*
1307 ==============
1308 CL_Clientinfo_f
1309 ==============
1310 */
CL_Clientinfo_f(void)1311 void CL_Clientinfo_f( void ) {
1312 	Com_Printf( "--------- Client Information ---------\n" );
1313 	Com_Printf( "state: %i\n", cls.state );
1314 	Com_Printf( "Server: %s\n", cls.servername );
1315 	Com_Printf ("User info settings:\n");
1316 	Info_Print( Cvar_InfoString( CVAR_USERINFO ) );
1317 	Com_Printf( "--------------------------------------\n" );
1318 }
1319 
1320 
1321 //====================================================================
1322 
1323 /*
1324 =================
1325 CL_DownloadsComplete
1326 
1327 Called when all downloading has been completed
1328 =================
1329 */
CL_DownloadsComplete(void)1330 void CL_DownloadsComplete( void ) {
1331 
1332 	// if we downloaded files we need to restart the file system
1333 	if (clc.downloadRestart) {
1334 		clc.downloadRestart = qfalse;
1335 
1336 		FS_Restart(clc.checksumFeed); // We possibly downloaded a pak, restart the file system to load it
1337 
1338 		// inform the server so we get new gamestate info
1339 		CL_AddReliableCommand( "donedl", qfalse );
1340 
1341 		// by sending the donedl command we request a new gamestate
1342 		// so we don't want to load stuff yet
1343 		return;
1344 	}
1345 
1346 	// let the client game init and load data
1347 	cls.state = CA_LOADING;
1348 
1349 	// Pump the loop, this may change gamestate!
1350 	Com_EventLoop();
1351 
1352 	// if the gamestate was changed by calling Com_EventLoop
1353 	// then we loaded everything already and we don't want to do it again.
1354 	if ( cls.state != CA_LOADING ) {
1355 		return;
1356 	}
1357 
1358 	// starting to load a map so we get out of full screen ui mode
1359 	Cvar_Set("r_uiFullScreen", "0");
1360 
1361 	// flush client memory and start loading stuff
1362 	// this will also (re)load the UI
1363 	// if this is a local client then only the client part of the hunk
1364 	// will be cleared, note that this is done after the hunk mark has been set
1365 	CL_FlushMemory();
1366 
1367 	// initialize the CGame
1368 	cls.cgameStarted = qtrue;
1369 	CL_InitCGame();
1370 
1371 	// set pure checksums
1372 	CL_SendPureChecksums();
1373 
1374 	CL_WritePacket();
1375 	CL_WritePacket();
1376 	CL_WritePacket();
1377 }
1378 
1379 /*
1380 =================
1381 CL_BeginDownload
1382 
1383 Requests a file to download from the server.  Stores it in the current
1384 game directory.
1385 =================
1386 */
1387 
CL_BeginDownload(const char * localName,const char * remoteName)1388 void CL_BeginDownload( const char *localName, const char *remoteName ) {
1389 
1390 	Com_DPrintf("***** CL_BeginDownload *****\n"
1391 				"Localname: %s\n"
1392 				"Remotename: %s\n"
1393 				"****************************\n", localName, remoteName);
1394 
1395 	Q_strncpyz ( clc.downloadName, localName, sizeof(clc.downloadName) );
1396 	Com_sprintf( clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName );
1397 
1398 	// Set so UI gets access to it
1399 	Cvar_Set( "cl_downloadName", remoteName );
1400 	Cvar_Set( "cl_downloadSize", "0" );
1401 	Cvar_Set( "cl_downloadCount", "0" );
1402 	Cvar_SetValue( "cl_downloadTime", (float) cls.realtime );
1403 
1404 	clc.downloadBlock = 0; // Starting new file
1405 	clc.downloadCount = 0;
1406 
1407 	CL_AddReliableCommand( va("download %s", remoteName), qfalse );
1408 }
1409 
1410 /*
1411 =================
1412 CL_NextDownload
1413 
1414 A download completed or failed
1415 =================
1416 */
CL_NextDownload(void)1417 void CL_NextDownload(void) {
1418 	char *s;
1419 	char *remoteName, *localName;
1420 
1421 	// A download has finished, check whether this matches a referenced checksum
1422 	if(*clc.downloadName)
1423 	{
1424 		char *zippath = FS_BuildOSPath(Cvar_VariableString("fs_homepath"), clc.downloadName, "");
1425 		zippath[strlen(zippath)-1] = '\0';
1426 
1427 		if(!FS_CompareZipChecksum(zippath))
1428 			Com_Error(ERR_DROP, "Incorrect checksum for file: %s", clc.downloadName);
1429 	}
1430 
1431 	*clc.downloadTempName = *clc.downloadName = 0;
1432 	Cvar_Set("cl_downloadName", "");
1433 
1434 	// We are looking to start a download here
1435 	if (*clc.downloadList) {
1436 		s = clc.downloadList;
1437 
1438 		// format is:
1439 		//  @remotename@localname@remotename@localname, etc.
1440 
1441 		if (*s == '@')
1442 			s++;
1443 		remoteName = s;
1444 
1445 		if ( (s = strchr(s, '@')) == NULL ) {
1446 			CL_DownloadsComplete();
1447 			return;
1448 		}
1449 
1450 		*s++ = 0;
1451 		localName = s;
1452 		if ( (s = strchr(s, '@')) != NULL )
1453 			*s++ = 0;
1454 		else
1455 			s = localName + strlen(localName); // point at the nul byte
1456 
1457 		if (!cl_allowDownload->integer) {
1458 			Com_Error(ERR_DROP, "UDP Downloads are disabled on your client. (cl_allowDownload is %d)", cl_allowDownload->integer);
1459 			return;
1460 		}
1461 		else {
1462 			CL_BeginDownload( localName, remoteName );
1463 		}
1464 
1465 		clc.downloadRestart = qtrue;
1466 
1467 		// move over the rest
1468 		memmove( clc.downloadList, s, strlen(s) + 1);
1469 
1470 		return;
1471 	}
1472 
1473 	CL_DownloadsComplete();
1474 }
1475 
1476 /*
1477 =================
1478 CL_InitDownloads
1479 
1480 After receiving a valid game state, we valid the cgame and local zip files here
1481 and determine if we need to download them
1482 =================
1483 */
CL_InitDownloads(void)1484 void CL_InitDownloads(void) {
1485   char missingfiles[1024];
1486 
1487 	if ( !cl_allowDownload->integer )
1488 	{
1489 		// autodownload is disabled on the client
1490 		// but it's possible that some referenced files on the server are missing
1491 		if (FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) )
1492 		{
1493 			// NOTE TTimo I would rather have that printed as a modal message box
1494 			//   but at this point while joining the game we don't know wether we will successfully join or not
1495 			Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s"
1496 				"You might not be able to join the game\n"
1497 				"Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles );
1498 		}
1499 	}
1500 	else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ) , qtrue ) ) {
1501 
1502 		Com_Printf("Need paks: %s\n", clc.downloadList );
1503 
1504 		if ( *clc.downloadList ) {
1505 			// if autodownloading is not enabled on the server
1506 			cls.state = CA_CONNECTED;
1507 
1508 			*clc.downloadTempName = *clc.downloadName = 0;
1509 			Cvar_Set( "cl_downloadName", "" );
1510 
1511 			CL_NextDownload();
1512 			return;
1513 		}
1514 
1515 	}
1516 	CL_DownloadsComplete();
1517 }
1518 
1519 /*
1520 =================
1521 CL_CheckForResend
1522 
1523 Resend a connect message if the last one has timed out
1524 =================
1525 */
CL_CheckForResend(void)1526 void CL_CheckForResend( void ) {
1527 	int		port;
1528 	char	info[MAX_INFO_STRING];
1529 	char	data[MAX_INFO_STRING+10];
1530 
1531 	// don't send anything if playing back a demo
1532 	if ( clc.demoplaying ) {
1533 		return;
1534 	}
1535 
1536 	// resend if we haven't gotten a reply yet
1537 	if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) {
1538 		return;
1539 	}
1540 
1541 	if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) {
1542 		return;
1543 	}
1544 
1545 	clc.connectTime = cls.realtime;	// for retransmit requests
1546 	clc.connectPacketCount++;
1547 
1548 
1549 	switch ( cls.state ) {
1550 	case CA_CONNECTING:
1551 		// requesting a challenge
1552 
1553 		// The challenge request shall be followed by a client challenge so no malicious server can hijack this connection.
1554 		Com_sprintf(data, sizeof(data), "getchallenge %d", clc.challenge);
1555 
1556 		NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, data);
1557 		break;
1558 
1559 	case CA_CHALLENGING:
1560 		// sending back the challenge
1561 		port = (int) Cvar_VariableValue ("net_qport");
1562 
1563 		Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
1564 		Info_SetValueForKey( info, "protocol", va("%i", PROTOCOL_VERSION ) );
1565 		Info_SetValueForKey( info, "qport", va("%i", port ) );
1566 		Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) );
1567 
1568 		Com_sprintf(data, sizeof(data), "connect \"%s\"", info );
1569 		NET_OutOfBandData( NS_CLIENT, clc.serverAddress, (byte *)data, strlen(data) );
1570 
1571 		// the most current userinfo has been sent, so watch for any
1572 		// newer changes to userinfo variables
1573 		cvar_modifiedFlags &= ~CVAR_USERINFO;
1574 		break;
1575 
1576 	default:
1577 		Com_Error( ERR_FATAL, "CL_CheckForResend: bad cls.state" );
1578 	}
1579 }
1580 
1581 
1582 /*
1583 ===================
1584 CL_DisconnectPacket
1585 
1586 Sometimes the server can drop the client and the netchan based
1587 disconnect can be lost.  If the client continues to send packets
1588 to the server, the server will send out of band disconnect packets
1589 to the client so it doesn't have to wait for the full timeout period.
1590 ===================
1591 */
CL_DisconnectPacket(netadr_t from)1592 void CL_DisconnectPacket( netadr_t from ) {
1593 	if ( cls.state < CA_AUTHORIZING ) {
1594 		return;
1595 	}
1596 
1597 	// if not from our server, ignore it
1598 	if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
1599 		return;
1600 	}
1601 
1602 	// if we have received packets within three seconds, ignore it
1603 	// (it might be a malicious spoof)
1604 	if ( cls.realtime - clc.lastPacketTime < 3000 ) {
1605 		return;
1606 	}
1607 
1608 	// drop the connection (FIXME: connection dropped dialog)
1609 	Com_Printf( "Server disconnected for unknown reason\n" );
1610 
1611 	CL_Disconnect( qtrue );
1612 }
1613 
1614 
1615 /*
1616 ===================
1617 CL_MotdPacket
1618 
1619 ===================
1620 */
CL_MotdPacket(netadr_t from)1621 void CL_MotdPacket( netadr_t from ) {
1622 	char	*challenge;
1623 	char	*info;
1624 
1625 	// if not from our server, ignore it
1626 	if ( !NET_CompareAdr( from, cls.updateServer ) ) {
1627 		return;
1628 	}
1629 
1630 	info = Cmd_Argv(1);
1631 
1632 	// check challenge
1633 	challenge = Info_ValueForKey( info, "challenge" );
1634 	if ( strcmp( challenge, cls.updateChallenge ) ) {
1635 		return;
1636 	}
1637 
1638 	challenge = Info_ValueForKey( info, "motd" );
1639 
1640 	Q_strncpyz( cls.updateInfoString, info, sizeof( cls.updateInfoString ) );
1641 	Cvar_Set( "cl_motdString", challenge );
1642 }
1643 
1644 /*
1645 ===================
1646 CL_InitServerInfo
1647 ===================
1648 */
CL_InitServerInfo(serverInfo_t * server,netadr_t * address)1649 void CL_InitServerInfo( serverInfo_t *server, netadr_t *address ) {
1650 	server->adr = *address;
1651 	server->clients = 0;
1652 	server->hostName[0] = '\0';
1653 	server->mapName[0] = '\0';
1654 	server->maxClients = 0;
1655 	server->maxPing = 0;
1656 	server->minPing = 0;
1657 	server->netType = 0;
1658 	server->needPassword = qfalse;
1659 	server->trueJedi = 0;
1660 	server->weaponDisable = 0;
1661 	server->forceDisable = 0;
1662 	server->ping = -1;
1663 	server->game[0] = '\0';
1664 	server->gameType = 0;
1665 	server->humans = server->bots = 0;
1666 }
1667 
1668 #define MAX_SERVERSPERPACKET	256
1669 
1670 /*
1671 ===================
1672 CL_ServersResponsePacket
1673 ===================
1674 */
CL_ServersResponsePacket(const netadr_t * from,msg_t * msg)1675 void CL_ServersResponsePacket( const netadr_t *from, msg_t *msg ) {
1676 	int				i, j, count, total;
1677 	netadr_t addresses[MAX_SERVERSPERPACKET];
1678 	int				numservers;
1679 	byte*			buffptr;
1680 	byte*			buffend;
1681 
1682 	Com_Printf("CL_ServersResponsePacket from %s\n", NET_AdrToString( *from ) );
1683 
1684 	if (cls.numglobalservers == -1) {
1685 		// state to detect lack of servers or lack of response
1686 		cls.numglobalservers = 0;
1687 		cls.numGlobalServerAddresses = 0;
1688 	}
1689 
1690 	// parse through server response string
1691 	numservers = 0;
1692 	buffptr    = msg->data;
1693 	buffend    = buffptr + msg->cursize;
1694 
1695 	// advance to initial token
1696 	do
1697 	{
1698 		if(*buffptr == '\\')
1699 			break;
1700 
1701 		buffptr++;
1702 	} while (buffptr < buffend);
1703 
1704 	while (buffptr + 1 < buffend)
1705 	{
1706 		// IPv4 address
1707 		if (*buffptr == '\\')
1708 		{
1709 			buffptr++;
1710 
1711 			if (buffend - buffptr < (int)(sizeof(addresses[numservers].ip) + sizeof(addresses[numservers].port) + 1))
1712 				break;
1713 
1714 			for(size_t i = 0; i < sizeof(addresses[numservers].ip); i++)
1715 				addresses[numservers].ip[i] = *buffptr++;
1716 
1717 			addresses[numservers].type = NA_IP;
1718 		}
1719 		else
1720 			// syntax error!
1721 			break;
1722 
1723 		// parse out port
1724 		addresses[numservers].port = (*buffptr++) << 8;
1725 		addresses[numservers].port += *buffptr++;
1726 		addresses[numservers].port = BigShort( addresses[numservers].port );
1727 
1728 		// syntax check
1729 		if (*buffptr != '\\')
1730 			break;
1731 
1732 		numservers++;
1733 		if (numservers >= MAX_SERVERSPERPACKET)
1734 			break;
1735 	}
1736 
1737 	count = cls.numglobalservers;
1738 
1739 	for (i = 0; i < numservers && count < MAX_GLOBAL_SERVERS; i++) {
1740 		// build net address
1741 		serverInfo_t *server = &cls.globalServers[count];
1742 
1743 		// Tequila: It's possible to have sent many master server requests. Then
1744 		// we may receive many times the same addresses from the master server.
1745 		// We just avoid to add a server if it is still in the global servers list.
1746 		for (j = 0; j < count; j++)
1747 		{
1748 			if (NET_CompareAdr(cls.globalServers[j].adr, addresses[i]))
1749 				break;
1750 		}
1751 
1752 		if (j < count)
1753 			continue;
1754 
1755 		CL_InitServerInfo( server, &addresses[i] );
1756 		// advance to next slot
1757 		count++;
1758 	}
1759 
1760 	// if getting the global list
1761 	if ( count >= MAX_GLOBAL_SERVERS && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS )
1762 	{
1763 		// if we couldn't store the servers in the main list anymore
1764 		for (; i < numservers && cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS; i++)
1765 		{
1766 			// just store the addresses in an additional list
1767 			cls.globalServerAddresses[cls.numGlobalServerAddresses++] = addresses[i];
1768 		}
1769 	}
1770 
1771 	cls.numglobalservers = count;
1772 	total = count + cls.numGlobalServerAddresses;
1773 
1774 	Com_Printf("%d servers parsed (total %d)\n", numservers, total);
1775 }
1776 
1777 #ifndef MAX_STRINGED_SV_STRING
1778 #define MAX_STRINGED_SV_STRING 1024
1779 #endif
CL_CheckSVStringEdRef(char * buf,const char * str)1780 static void CL_CheckSVStringEdRef(char *buf, const char *str)
1781 { //I don't really like doing this. But it utilizes the system that was already in place.
1782 	int i = 0;
1783 	int b = 0;
1784 	int strLen = 0;
1785 	qboolean gotStrip = qfalse;
1786 
1787 	if (!str || !str[0])
1788 	{
1789 		if (str)
1790 		{
1791 			strcpy(buf, str);
1792 		}
1793 		return;
1794 	}
1795 
1796 	strcpy(buf, str);
1797 
1798 	strLen = strlen(str);
1799 
1800 	if (strLen >= MAX_STRINGED_SV_STRING)
1801 	{
1802 		return;
1803 	}
1804 
1805 	while (i < strLen && str[i])
1806 	{
1807 		gotStrip = qfalse;
1808 
1809 		if (str[i] == '@' && (i+1) < strLen)
1810 		{
1811 			if (str[i+1] == '@' && (i+2) < strLen)
1812 			{
1813 				if (str[i+2] == '@' && (i+3) < strLen)
1814 				{ //@@@ should mean to insert a stringed reference here, so insert it into buf at the current place
1815 					char stripRef[MAX_STRINGED_SV_STRING];
1816 					int r = 0;
1817 
1818 					while (i < strLen && str[i] == '@')
1819 					{
1820 						i++;
1821 					}
1822 
1823 					while (i < strLen && str[i] && str[i] != ' ' && str[i] != ':' && str[i] != '.' && str[i] != '\n')
1824 					{
1825 						stripRef[r] = str[i];
1826 						r++;
1827 						i++;
1828 					}
1829 					stripRef[r] = 0;
1830 
1831 					buf[b] = 0;
1832 					Q_strcat(buf, MAX_STRINGED_SV_STRING, SE_GetString(va("MP_SVGAME_%s", stripRef)));
1833 					b = strlen(buf);
1834 				}
1835 			}
1836 		}
1837 
1838 		if (!gotStrip)
1839 		{
1840 			buf[b] = str[i];
1841 			b++;
1842 		}
1843 		i++;
1844 	}
1845 
1846 	buf[b] = 0;
1847 }
1848 
1849 
1850 /*
1851 =================
1852 CL_ConnectionlessPacket
1853 
1854 Responses to broadcasts, etc
1855 =================
1856 */
CL_ConnectionlessPacket(netadr_t from,msg_t * msg)1857 void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
1858 	char	*s;
1859 	char	*c;
1860 	int challenge = 0;
1861 
1862 	MSG_BeginReadingOOB( msg );
1863 	MSG_ReadLong( msg );	// skip the -1
1864 
1865 	s = MSG_ReadStringLine( msg );
1866 
1867 	Cmd_TokenizeString( s );
1868 
1869 	c = Cmd_Argv( 0 );
1870 
1871 	if ( com_developer->integer ) {
1872 		Com_Printf( "CL packet %s: %s\n", NET_AdrToString( from ), c );
1873 	}
1874 
1875 	// challenge from the server we are connecting to
1876 	if ( !Q_stricmp(c, "challengeResponse") )
1877 	{
1878 		if ( cls.state != CA_CONNECTING )
1879 		{
1880 			Com_Printf( "Unwanted challenge response received.  Ignored.\n" );
1881 			return;
1882 		}
1883 
1884 		c = Cmd_Argv(2);
1885 		if(*c)
1886 			challenge = atoi(c);
1887 
1888 		if(!NET_CompareAdr(from, clc.serverAddress))
1889 		{
1890 			// This challenge response is not coming from the expected address.
1891 			// Check whether we have a matching client challenge to prevent
1892 			// connection hi-jacking.
1893 
1894 			if(!*c || challenge != clc.challenge)
1895 			{
1896 				Com_DPrintf("Challenge response received from unexpected source. Ignored.\n");
1897 				return;
1898 			}
1899 		}
1900 
1901 		// start sending challenge response instead of challenge request packets
1902 		clc.challenge = atoi(Cmd_Argv(1));
1903 		cls.state = CA_CHALLENGING;
1904 		clc.connectPacketCount = 0;
1905 		clc.connectTime = -99999;
1906 
1907 		// take this address as the new server address.  This allows
1908 		// a server proxy to hand off connections to multiple servers
1909 		clc.serverAddress = from;
1910 		Com_DPrintf ("challengeResponse: %d\n", clc.challenge);
1911 		return;
1912 	}
1913 
1914 	// server connection
1915 	if ( !Q_stricmp(c, "connectResponse") ) {
1916 		if ( cls.state >= CA_CONNECTED ) {
1917 			Com_Printf ("Dup connect received. Ignored.\n");
1918 			return;
1919 		}
1920 		if ( cls.state != CA_CHALLENGING ) {
1921 			Com_Printf ("connectResponse packet while not connecting. Ignored.\n");
1922 			return;
1923 		}
1924 		if ( !NET_CompareAdr( from, clc.serverAddress ) ) {
1925 			Com_Printf( "connectResponse from wrong address. Ignored.\n" );
1926 			return;
1927 		}
1928 		Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) );
1929 		cls.state = CA_CONNECTED;
1930 		clc.lastPacketSentTime = -9999;		// send first packet immediately
1931 		return;
1932 	}
1933 
1934 	// server responding to an info broadcast
1935 	if ( !Q_stricmp(c, "infoResponse") ) {
1936 		CL_ServerInfoPacket( from, msg );
1937 		return;
1938 	}
1939 
1940 	// server responding to a get playerlist
1941 	if ( !Q_stricmp(c, "statusResponse") ) {
1942 		CL_ServerStatusResponse( from, msg );
1943 		return;
1944 	}
1945 
1946 	// a disconnect message from the server, which will happen if the server
1947 	// dropped the connection but it is still getting packets from us
1948 	if (!Q_stricmp(c, "disconnect")) {
1949 		CL_DisconnectPacket( from );
1950 		return;
1951 	}
1952 
1953 	// echo request from server
1954 	if ( !Q_stricmp(c, "echo") ) {
1955 		NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) );
1956 		return;
1957 	}
1958 
1959 	// cd check
1960 	if ( !Q_stricmp(c, "keyAuthorize") ) {
1961 		// we don't use these now, so dump them on the floor
1962 		return;
1963 	}
1964 
1965 	// global MOTD from id
1966 	if ( !Q_stricmp(c, "motd") ) {
1967 		CL_MotdPacket( from );
1968 		return;
1969 	}
1970 
1971 	// echo request from server
1972 	if ( !Q_stricmp(c, "print") )
1973 	{
1974 		// NOTE: we may have to add exceptions for auth and update servers
1975 		if (NET_CompareAdr(from, clc.serverAddress) || NET_CompareAdr(from, rcon_address))
1976 		{
1977 			char sTemp[MAX_STRINGED_SV_STRING];
1978 
1979 			s = MSG_ReadString( msg );
1980 			CL_CheckSVStringEdRef(sTemp, s);
1981 			Q_strncpyz( clc.serverMessage, sTemp, sizeof( clc.serverMessage ) );
1982 			Com_Printf( "%s", sTemp );
1983 		}
1984 		return;
1985 	}
1986 
1987 	// list of servers sent back by a master server (classic)
1988 	if ( !Q_strncmp(c, "getserversResponse", 18) ) {
1989 		CL_ServersResponsePacket( &from, msg );
1990 		return;
1991 	}
1992 
1993 	Com_DPrintf ("Unknown connectionless packet command.\n");
1994 }
1995 
1996 
1997 /*
1998 =================
1999 CL_PacketEvent
2000 
2001 A packet has arrived from the main event loop
2002 =================
2003 */
CL_PacketEvent(netadr_t from,msg_t * msg)2004 void CL_PacketEvent( netadr_t from, msg_t *msg ) {
2005 	int		headerBytes;
2006 
2007 	clc.lastPacketTime = cls.realtime;
2008 
2009 	if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) {
2010 		CL_ConnectionlessPacket( from, msg );
2011 		return;
2012 	}
2013 
2014 	if ( cls.state < CA_CONNECTED ) {
2015 		return;		// can't be a valid sequenced packet
2016 	}
2017 
2018 	if ( msg->cursize < 4 ) {
2019 		Com_Printf ("%s: Runt packet\n",NET_AdrToString( from ));
2020 		return;
2021 	}
2022 
2023 	//
2024 	// packet from server
2025 	//
2026 	if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
2027 		if ( com_developer->integer ) {
2028 			Com_Printf( "%s:sequenced packet without connection\n",
2029 				NET_AdrToString( from ) );
2030 		}
2031 		// FIXME: send a client disconnect?
2032 		return;
2033 	}
2034 
2035 	if (!CL_Netchan_Process( &clc.netchan, msg) ) {
2036 		return;		// out of order, duplicated, etc
2037 	}
2038 
2039 	// the header is different lengths for reliable and unreliable messages
2040 	headerBytes = msg->readcount;
2041 
2042 	// track the last message received so it can be returned in
2043 	// client messages, allowing the server to detect a dropped
2044 	// gamestate
2045 	clc.serverMessageSequence = LittleLong( *(int *)msg->data );
2046 
2047 	clc.lastPacketTime = cls.realtime;
2048 	CL_ParseServerMessage( msg );
2049 
2050 	//
2051 	// we don't know if it is ok to save a demo message until
2052 	// after we have parsed the frame
2053 	//
2054 	if ( clc.demorecording && !clc.demowaiting ) {
2055 		CL_WriteDemoMessage( msg, headerBytes );
2056 	}
2057 }
2058 
2059 /*
2060 ==================
2061 CL_CheckTimeout
2062 
2063 ==================
2064 */
CL_CheckTimeout(void)2065 void CL_CheckTimeout( void ) {
2066 	//
2067 	// check timeout
2068 	//
2069 	if ( ( !CL_CheckPaused() || !sv_paused->integer )
2070 		&& cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC
2071 	    && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) {
2072 		if (++cl.timeoutcount > 5) {	// timeoutcount saves debugger
2073 			const char *psTimedOut = SE_GetString("MP_SVGAME_SERVER_CONNECTION_TIMED_OUT");
2074 			Com_Printf ("\n%s\n",psTimedOut);
2075 			Com_Error(ERR_DROP, psTimedOut);
2076 			//CL_Disconnect( qtrue );
2077 			return;
2078 		}
2079 	} else {
2080 		cl.timeoutcount = 0;
2081 	}
2082 }
2083 
2084 /*
2085 ==================
2086 CL_CheckPaused
2087 Check whether client has been paused.
2088 ==================
2089 */
CL_CheckPaused(void)2090 qboolean CL_CheckPaused(void)
2091 {
2092 	// if cl_paused->modified is set, the cvar has only been changed in
2093 	// this frame. Keep paused in this frame to ensure the server doesn't
2094 	// lag behind.
2095 	if(cl_paused->integer || cl_paused->modified)
2096 		return qtrue;
2097 
2098 	return qfalse;
2099 }
2100 
2101 //============================================================================
2102 
2103 /*
2104 ==================
2105 CL_CheckUserinfo
2106 
2107 ==================
2108 */
CL_CheckUserinfo(void)2109 void CL_CheckUserinfo( void ) {
2110 	// don't add reliable commands when not yet connected
2111 	if ( cls.state < CA_CONNECTED ) {
2112 		return;
2113 	}
2114 	// don't overflow the reliable command buffer when paused
2115 	if ( CL_CheckPaused() ) {
2116 		return;
2117 	}
2118 	// send a reliable userinfo update if needed
2119 	if ( cvar_modifiedFlags & CVAR_USERINFO ) {
2120 		cvar_modifiedFlags &= ~CVAR_USERINFO;
2121 		CL_AddReliableCommand( va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ), qfalse );
2122 	}
2123 
2124 }
2125 
2126 /*
2127 ==================
2128 CL_Frame
2129 
2130 ==================
2131 */
2132 static unsigned int frameCount;
2133 static float avgFrametime=0.0;
2134 extern void SE_CheckForLanguageUpdates(void);
CL_Frame(int msec)2135 void CL_Frame ( int msec ) {
2136 	qboolean takeVideoFrame = qfalse;
2137 
2138 	if ( !com_cl_running->integer ) {
2139 		return;
2140 	}
2141 
2142 	SE_CheckForLanguageUpdates();	// will take zero time to execute unless language changes, then will reload strings.
2143 									//	of course this still doesn't work for menus...
2144 
2145 	if ( cls.state == CA_DISCONNECTED && !( Key_GetCatcher( ) & KEYCATCH_UI )
2146 		&& !com_sv_running->integer && cls.uiStarted ) {
2147 		// if disconnected, bring up the menu
2148 		S_StopAllSounds();
2149 		UIVM_SetActiveMenu( UIMENU_MAIN );
2150 	}
2151 
2152 	// if recording an avi, lock to a fixed fps
2153 	if ( CL_VideoRecording( ) && cl_aviFrameRate->integer && msec) {
2154 		if ( cls.state == CA_ACTIVE || cl_forceavidemo->integer) {
2155 			float fps = Q_min(cl_aviFrameRate->value * com_timescale->value, 1000.0f);
2156 			float frameDuration = Q_max(1000.0f / fps, 1.0f) + clc.aviVideoFrameRemainder;
2157 			takeVideoFrame = qtrue;
2158 
2159 			msec = (int)frameDuration;
2160 			clc.aviVideoFrameRemainder = frameDuration - msec;
2161 		}
2162 	}
2163 
2164 	// save the msec before checking pause
2165 	cls.realFrametime = msec;
2166 
2167 	// decide the simulation time
2168 	cls.frametime = msec;
2169 	if(cl_framerate->integer)
2170 	{
2171 		avgFrametime+=msec;
2172 		char mess[256];
2173 		if(!(frameCount&0x1f))
2174 		{
2175 			Com_sprintf(mess,sizeof(mess),"Frame rate=%f\n\n",1000.0f*(1.0/(avgFrametime/32.0f)));
2176 	//		Com_OPrintf("%s", mess);
2177 			Com_Printf("%s", mess);
2178 			avgFrametime=0.0f;
2179 		}
2180 		frameCount++;
2181 	}
2182 
2183 	cls.realtime += cls.frametime;
2184 
2185 	if ( cl_timegraph->integer ) {
2186 		SCR_DebugGraph ( cls.realFrametime * 0.25, 0 );
2187 	}
2188 
2189 	// see if we need to update any userinfo
2190 	CL_CheckUserinfo();
2191 
2192 	// if we haven't gotten a packet in a long time,
2193 	// drop the connection
2194 	CL_CheckTimeout();
2195 
2196 	// send intentions now
2197 	CL_SendCmd();
2198 
2199 	// resend a connection request if necessary
2200 	CL_CheckForResend();
2201 
2202 	// decide on the serverTime to render
2203 	CL_SetCGameTime();
2204 
2205 	// update the screen
2206 	SCR_UpdateScreen();
2207 
2208 	// update audio
2209 	S_Update();
2210 
2211 	// advance local effects for next frame
2212 	SCR_RunCinematic();
2213 
2214 	Con_RunConsole();
2215 
2216 	// reset the heap for Ghoul2 vert transform space gameside
2217 	if (G2VertSpaceServer)
2218 	{
2219 		G2VertSpaceServer->ResetHeap();
2220 	}
2221 
2222 	cls.framecount++;
2223 
2224 	if ( takeVideoFrame ) {
2225 		// save the current screen
2226 		CL_TakeVideoFrame( );
2227 	}
2228 }
2229 
2230 
2231 //============================================================================
2232 
2233 /*
2234 ================
2235 CL_RefPrintf
2236 
2237 DLL glue
2238 ================
2239 */
CL_RefPrintf(int print_level,const char * fmt,...)2240 void QDECL CL_RefPrintf( int print_level, const char *fmt, ...) {
2241 	va_list		argptr;
2242 	char		msg[MAXPRINTMSG];
2243 
2244 	va_start (argptr,fmt);
2245 	Q_vsnprintf(msg, sizeof(msg), fmt, argptr);
2246 	va_end (argptr);
2247 
2248 	if ( print_level == PRINT_ALL ) {
2249 		Com_Printf ("%s", msg);
2250 	} else if ( print_level == PRINT_WARNING ) {
2251 		Com_Printf (S_COLOR_YELLOW "%s", msg);		// yellow
2252 	} else if ( print_level == PRINT_DEVELOPER ) {
2253 		Com_DPrintf (S_COLOR_RED "%s", msg);		// red
2254 	}
2255 }
2256 
2257 
2258 
2259 /*
2260 ============
2261 CL_ShutdownRef
2262 ============
2263 */
CL_ShutdownRef(qboolean restarting)2264 static void CL_ShutdownRef( qboolean restarting ) {
2265 	if ( re )
2266 	{
2267 		if ( re->Shutdown )
2268 		{
2269 			re->Shutdown( qtrue, restarting );
2270 		}
2271 	}
2272 
2273 	re = NULL;
2274 
2275 	if ( rendererLib != NULL ) {
2276 		Sys_UnloadDll (rendererLib);
2277 		rendererLib = NULL;
2278 	}
2279 }
2280 
2281 /*
2282 ============
2283 CL_InitRenderer
2284 ============
2285 */
CL_InitRenderer(void)2286 void CL_InitRenderer( void ) {
2287 	// this sets up the renderer and calls R_Init
2288 	re->BeginRegistration( &cls.glconfig );
2289 
2290 	// load character sets
2291 	cls.charSetShader = re->RegisterShaderNoMip("gfx/2d/charsgrid_med");
2292 
2293 	cls.whiteShader = re->RegisterShader( "white" );
2294 	cls.consoleShader = re->RegisterShader( "console" );
2295 	g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2;
2296 	g_consoleField.widthInChars = g_console_field_width;
2297 }
2298 
2299 /*
2300 ============================
2301 CL_StartHunkUsers
2302 
2303 After the server has cleared the hunk, these will need to be restarted
2304 This is the only place that any of these functions are called from
2305 ============================
2306 */
CL_StartHunkUsers(void)2307 void CL_StartHunkUsers( void ) {
2308 	if (!com_cl_running) {
2309 		return;
2310 	}
2311 
2312 	if ( !com_cl_running->integer ) {
2313 		return;
2314 	}
2315 
2316 	if ( !cls.rendererStarted ) {
2317 		cls.rendererStarted = qtrue;
2318 		CL_InitRenderer();
2319 	}
2320 
2321 	if ( !cls.soundStarted ) {
2322 		cls.soundStarted = qtrue;
2323 		S_Init();
2324 	}
2325 
2326 	if ( !cls.soundRegistered ) {
2327 		cls.soundRegistered = qtrue;
2328 		S_BeginRegistration();
2329 	}
2330 
2331 	if ( !cls.uiStarted ) {
2332 		cls.uiStarted = qtrue;
2333 		CL_InitUI();
2334 	}
2335 }
2336 
2337 /*
2338 ============
2339 CL_InitRef
2340 ============
2341 */
2342 qboolean Com_TheHunkMarkHasBeenMade(void);
2343 
2344 //qcommon/cm_load.cpp
2345 extern void *gpvCachedMapDiskImage;
2346 extern qboolean gbUsingCachedMapDataRightNow;
2347 
GetSharedMemory(void)2348 static char *GetSharedMemory( void ) { return cl.mSharedMemory; }
GetCurrentVM(void)2349 static vm_t *GetCurrentVM( void ) { return currentVM; }
CGVMLoaded(void)2350 static qboolean CGVMLoaded( void ) { return (qboolean)cls.cgameStarted; }
CM_GetCachedMapDiskImage(void)2351 static void *CM_GetCachedMapDiskImage( void ) { return gpvCachedMapDiskImage; }
CM_SetCachedMapDiskImage(void * ptr)2352 static void CM_SetCachedMapDiskImage( void *ptr ) { gpvCachedMapDiskImage = ptr; }
CM_SetUsingCache(qboolean usingCache)2353 static void CM_SetUsingCache( qboolean usingCache ) { gbUsingCachedMapDataRightNow = usingCache; }
2354 
2355 #define G2_VERT_SPACE_SERVER_SIZE 256
2356 IHeapAllocator *G2VertSpaceServer = NULL;
2357 CMiniHeap IHeapAllocator_singleton(G2_VERT_SPACE_SERVER_SIZE * 1024);
2358 
GetG2VertSpaceServer(void)2359 static IHeapAllocator *GetG2VertSpaceServer( void ) {
2360 	return G2VertSpaceServer;
2361 }
2362 
2363 #define DEFAULT_RENDER_LIBRARY "rd-vanilla"
2364 
CL_InitRef(void)2365 void CL_InitRef( void ) {
2366 	static refimport_t ri;
2367 	refexport_t	*ret;
2368 	GetRefAPI_t	GetRefAPI;
2369 	char		dllName[MAX_OSPATH];
2370 
2371 	Com_Printf( "----- Initializing Renderer ----\n" );
2372 
2373 	cl_renderer = Cvar_Get( "cl_renderer", DEFAULT_RENDER_LIBRARY, CVAR_ARCHIVE|CVAR_LATCH|CVAR_PROTECTED, "Which renderer library to use" );
2374 
2375 	Com_sprintf( dllName, sizeof( dllName ), "%s_" ARCH_STRING DLL_EXT, cl_renderer->string );
2376 
2377 	if( !(rendererLib = Sys_LoadDll( dllName, qfalse )) && strcmp( cl_renderer->string, cl_renderer->resetString ) )
2378 	{
2379 		Com_Printf( "failed: trying to load fallback renderer\n" );
2380 		Cvar_ForceReset( "cl_renderer" );
2381 
2382 		Com_sprintf( dllName, sizeof( dllName ), DEFAULT_RENDER_LIBRARY "_" ARCH_STRING DLL_EXT );
2383 		rendererLib = Sys_LoadDll( dllName, qfalse );
2384 	}
2385 
2386 	if ( !rendererLib ) {
2387 		Com_Error( ERR_FATAL, "Failed to load renderer\n" );
2388 	}
2389 
2390 	memset( &ri, 0, sizeof( ri ) );
2391 
2392 	GetRefAPI = (GetRefAPI_t)Sys_LoadFunction( rendererLib, "GetRefAPI" );
2393 	if ( !GetRefAPI )
2394 		Com_Error( ERR_FATAL, "Can't load symbol GetRefAPI: '%s'", Sys_LibraryError() );
2395 
2396 	//set up the import table
2397 	ri.Printf = CL_RefPrintf;
2398 	ri.Error = Com_Error;
2399 	ri.OPrintf = Com_OPrintf;
2400 	ri.Milliseconds = Sys_Milliseconds2; //FIXME: unix+mac need this
2401 	ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory;
2402 	ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory;
2403 	ri.Hunk_Alloc = Hunk_Alloc;
2404 	ri.Hunk_MemoryRemaining = Hunk_MemoryRemaining;
2405 	ri.Z_Malloc = Z_Malloc;
2406 	ri.Z_Free = Z_Free;
2407 	ri.Z_MemSize = Z_MemSize;
2408 	ri.Z_MorphMallocTag = Z_MorphMallocTag;
2409 	ri.Cmd_ExecuteString = Cmd_ExecuteString;
2410 	ri.Cmd_Argc = Cmd_Argc;
2411 	ri.Cmd_Argv = Cmd_Argv;
2412 	ri.Cmd_ArgsBuffer = Cmd_ArgsBuffer;
2413 	ri.Cmd_AddCommand = Cmd_AddCommand;
2414 	ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
2415 	ri.Cvar_Set = Cvar_Set;
2416 	ri.Cvar_Get = Cvar_Get;
2417 	ri.Cvar_SetValue = Cvar_SetValue;
2418 	ri.Cvar_CheckRange = Cvar_CheckRange;
2419 	ri.Cvar_VariableStringBuffer = Cvar_VariableStringBuffer;
2420 	ri.Cvar_VariableString = Cvar_VariableString;
2421 	ri.Cvar_VariableValue = Cvar_VariableValue;
2422 	ri.Cvar_VariableIntegerValue = Cvar_VariableIntegerValue;
2423 	ri.Sys_LowPhysicalMemory = Sys_LowPhysicalMemory;
2424 	ri.SE_GetString = SE_GetString;
2425 	ri.FS_FreeFile = FS_FreeFile;
2426 	ri.FS_FreeFileList = FS_FreeFileList;
2427 	ri.FS_Read = FS_Read;
2428 	ri.FS_ReadFile = FS_ReadFile;
2429 	ri.FS_FCloseFile = FS_FCloseFile;
2430 	ri.FS_FOpenFileRead = FS_FOpenFileRead;
2431 	ri.FS_FOpenFileWrite = FS_FOpenFileWrite;
2432 	ri.FS_FOpenFileByMode = FS_FOpenFileByMode;
2433 	ri.FS_FileExists = FS_FileExists;
2434 	ri.FS_FileIsInPAK = FS_FileIsInPAK;
2435 	ri.FS_ListFiles = FS_ListFiles;
2436 	ri.FS_Write = FS_Write;
2437 	ri.FS_WriteFile = FS_WriteFile;
2438 	ri.CM_BoxTrace = CM_BoxTrace;
2439 	ri.CM_DrawDebugSurface = CM_DrawDebugSurface;
2440 	ri.CM_CullWorldBox = CM_CullWorldBox;
2441 	ri.CM_ClusterPVS = CM_ClusterPVS;
2442 	ri.CM_LeafArea = CM_LeafArea;
2443 	ri.CM_LeafCluster = CM_LeafCluster;
2444 	ri.CM_PointLeafnum = CM_PointLeafnum;
2445 	ri.CM_PointContents = CM_PointContents;
2446 	ri.Com_TheHunkMarkHasBeenMade = Com_TheHunkMarkHasBeenMade;
2447 	ri.S_RestartMusic = S_RestartMusic;
2448 	ri.SND_RegisterAudio_LevelLoadEnd = SND_RegisterAudio_LevelLoadEnd;
2449 	ri.CIN_RunCinematic = CIN_RunCinematic;
2450 	ri.CIN_PlayCinematic = CIN_PlayCinematic;
2451 	ri.CIN_UploadCinematic = CIN_UploadCinematic;
2452 	ri.CL_WriteAVIVideoFrame = CL_WriteAVIVideoFrame;
2453 
2454 	// g2 data access
2455 	ri.GetSharedMemory = GetSharedMemory;
2456 
2457 	// (c)g vm callbacks
2458 	ri.GetCurrentVM = GetCurrentVM;
2459 	ri.CGVMLoaded = CGVMLoaded;
2460 	ri.CGVM_RagCallback = CGVM_RagCallback;
2461 
2462     ri.WIN_Init = WIN_Init;
2463 	ri.WIN_SetGamma = WIN_SetGamma;
2464     ri.WIN_Shutdown = WIN_Shutdown;
2465     ri.WIN_Present = WIN_Present;
2466 	ri.GL_GetProcAddress = WIN_GL_GetProcAddress;
2467 	ri.GL_ExtensionSupported = WIN_GL_ExtensionSupported;
2468 
2469 	ri.CM_GetCachedMapDiskImage = CM_GetCachedMapDiskImage;
2470 	ri.CM_SetCachedMapDiskImage = CM_SetCachedMapDiskImage;
2471 	ri.CM_SetUsingCache = CM_SetUsingCache;
2472 
2473 	//FIXME: Might have to do something about this...
2474 	ri.GetG2VertSpaceServer = GetG2VertSpaceServer;
2475 	G2VertSpaceServer = &IHeapAllocator_singleton;
2476 
2477 	ri.PD_Store = PD_Store;
2478 	ri.PD_Load = PD_Load;
2479 
2480 	ret = GetRefAPI( REF_API_VERSION, &ri );
2481 
2482 //	Com_Printf( "-------------------------------\n");
2483 
2484 	if ( !ret ) {
2485 		Com_Error (ERR_FATAL, "Couldn't initialize refresh" );
2486 	}
2487 
2488 	re = ret;
2489 
2490 	// unpause so the cgame definately gets a snapshot and renders a frame
2491 	Cvar_Set( "cl_paused", "0" );
2492 }
2493 
2494 
2495 //===========================================================================================
2496 
2497 #define MODEL_CHANGE_DELAY 5000
2498 int gCLModelDelay = 0;
2499 
CL_SetModel_f(void)2500 void CL_SetModel_f( void ) {
2501 	char	*arg;
2502 	char	name[256];
2503 
2504 	arg = Cmd_Argv( 1 );
2505 	if (arg[0])
2506 	{
2507 		/*
2508 		//If you wanted to be foolproof you would put this on the server I guess. But that
2509 		//tends to put things out of sync regarding cvar status. And I sort of doubt someone
2510 		//is going to write a client and figure out the protocol so that they can annoy people
2511 		//by changing models real fast.
2512 		int curTime = Com_Milliseconds();
2513 		if (gCLModelDelay > curTime)
2514 		{
2515 			Com_Printf("You can only change your model every %i seconds.\n", (MODEL_CHANGE_DELAY/1000));
2516 			return;
2517 		}
2518 
2519 		gCLModelDelay = curTime + MODEL_CHANGE_DELAY;
2520 		*/
2521 		//rwwFIXMEFIXME: This is currently broken and doesn't seem to work for connecting clients
2522 		Cvar_Set( "model", arg );
2523 	}
2524 	else
2525 	{
2526 		Cvar_VariableStringBuffer( "model", name, sizeof(name) );
2527 		Com_Printf("model is set to %s\n", name);
2528 	}
2529 }
2530 
CL_SetForcePowers_f(void)2531 void CL_SetForcePowers_f( void ) {
2532 	return;
2533 }
2534 
2535 /*
2536 ==================
2537 CL_VideoFilename
2538 ==================
2539 */
CL_VideoFilename(char * buf,int bufSize)2540 void CL_VideoFilename( char *buf, int bufSize ) {
2541 	time_t rawtime;
2542 	char timeStr[32] = {0}; // should really only reach ~19 chars
2543 
2544 	time( &rawtime );
2545 	strftime( timeStr, sizeof( timeStr ), "%Y-%m-%d_%H-%M-%S", localtime( &rawtime ) ); // or gmtime
2546 
2547 	Com_sprintf( buf, bufSize, "videos/video%s.avi", timeStr );
2548 }
2549 
2550 /*
2551 ===============
2552 CL_Video_f
2553 
2554 video
2555 video [filename]
2556 ===============
2557 */
CL_Video_f(void)2558 void CL_Video_f( void )
2559 {
2560 	char  filename[ MAX_OSPATH ];
2561 
2562 	if( !clc.demoplaying )
2563 	{
2564 		Com_Printf( "The video command can only be used when playing back demos\n" );
2565 		return;
2566 	}
2567 
2568 	if( Cmd_Argc( ) == 2 )
2569 	{
2570 		// explicit filename
2571 		Com_sprintf( filename, MAX_OSPATH, "videos/%s.avi", Cmd_Argv( 1 ) );
2572 	}
2573 	else
2574 	{
2575 		CL_VideoFilename( filename, MAX_OSPATH );
2576 
2577 		if ( FS_FileExists( filename ) ) {
2578 			Com_Printf( "Video: Couldn't create a file\n");
2579 			return;
2580  		}
2581 	}
2582 
2583 	CL_OpenAVIForWriting( filename );
2584 }
2585 
2586 /*
2587 ===============
2588 CL_StopVideo_f
2589 ===============
2590 */
CL_StopVideo_f(void)2591 void CL_StopVideo_f( void )
2592 {
2593 	CL_CloseAVI( );
2594 }
2595 
CL_AddFavorite_f(void)2596 static void CL_AddFavorite_f( void ) {
2597 	const bool connected = (cls.state == CA_ACTIVE) && !clc.demoplaying;
2598 	const int argc = Cmd_Argc();
2599 	if ( !connected && argc != 2 ) {
2600 		Com_Printf( "syntax: addFavorite <ip or hostname>\n" );
2601 		return;
2602 	}
2603 
2604 	const char *server = (argc == 2) ? Cmd_Argv( 1 ) : NET_AdrToString( clc.serverAddress );
2605 	const int status = LAN_AddFavAddr( server );
2606 	switch ( status ) {
2607 	case -1:
2608 		Com_Printf( "error adding favorite server: too many favorite servers\n" );
2609 		break;
2610 	case 0:
2611 		Com_Printf( "error adding favorite server: server already exists\n" );
2612 		break;
2613 	case 1:
2614 		Com_Printf( "successfully added favorite server \"%s\"\n", server );
2615 		break;
2616 	default:
2617 		Com_Printf( "unknown error (%i) adding favorite server\n", status );
2618 		break;
2619 	}
2620 }
2621 
2622 #define G2_VERT_SPACE_CLIENT_SIZE 256
2623 
2624 /*
2625 ===============
2626 CL_GenerateQKey
2627 
2628 test to see if a valid QKEY_FILE exists.  If one does not, try to generate
2629 it by filling it with 2048 bytes of random data.
2630 ===============
2631 */
2632 
CL_GenerateQKey(void)2633 static void CL_GenerateQKey(void)
2634 {
2635 	if (cl_enableGuid->integer) {
2636 		int len = 0;
2637 		unsigned char buff[ QKEY_SIZE ];
2638 		fileHandle_t f;
2639 
2640 		len = FS_SV_FOpenFileRead( QKEY_FILE, &f );
2641 		FS_FCloseFile( f );
2642 		if( len == QKEY_SIZE ) {
2643 			Com_Printf( "QKEY found.\n" );
2644 			return;
2645 		}
2646 		else {
2647 			if( len > 0 ) {
2648 				Com_Printf( "QKEY file size != %d, regenerating\n",
2649 					QKEY_SIZE );
2650 			}
2651 
2652 			Com_Printf( "QKEY building random string\n" );
2653 			Com_RandomBytes( buff, sizeof(buff) );
2654 
2655 			f = FS_SV_FOpenFileWrite( QKEY_FILE );
2656 			if( !f ) {
2657 				Com_Printf( "QKEY could not open %s for write\n",
2658 					QKEY_FILE );
2659 				return;
2660 			}
2661 			FS_Write( buff, sizeof(buff), f );
2662 			FS_FCloseFile( f );
2663 			Com_Printf( "QKEY generated\n" );
2664 		}
2665 	}
2666 }
2667 
2668 /*
2669 ====================
2670 CL_Init
2671 ====================
2672 */
CL_Init(void)2673 void CL_Init( void ) {
2674 //	Com_Printf( "----- Client Initialization -----\n" );
2675 
2676 	Con_Init ();
2677 
2678 	CL_ClearState ();
2679 
2680 	cls.state = CA_DISCONNECTED;	// no longer CA_UNINITIALIZED
2681 
2682 	cls.realtime = 0;
2683 
2684 	CL_InitInput ();
2685 
2686 	//
2687 	// register our variables
2688 	//
2689 	cl_noprint = Cvar_Get( "cl_noprint", "0", 0 );
2690 	cl_motd = Cvar_Get ("cl_motd", "1", CVAR_ARCHIVE_ND, "Display welcome message from master server on the bottom of connection screen" );
2691 	cl_motdServer[0] = Cvar_Get( "cl_motdServer1", UPDATE_SERVER_NAME, 0 );
2692 	cl_motdServer[1] = Cvar_Get( "cl_motdServer2", JKHUB_UPDATE_SERVER_NAME, 0 );
2693 	for ( int index = 2; index < MAX_MASTER_SERVERS; index++ )
2694 		cl_motdServer[index] = Cvar_Get( va( "cl_motdServer%d", index + 1 ), "", CVAR_ARCHIVE_ND );
2695 
2696 	cl_timeout = Cvar_Get ("cl_timeout", "200", 0);
2697 
2698 	cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP );
2699 	cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP );
2700 	cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP );
2701 	cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP );
2702 	cl_freezeDemo = Cvar_Get ("cl_freezeDemo", "0", CVAR_TEMP );
2703 	rcon_client_password = Cvar_Get ("rconPassword", "", CVAR_TEMP, "Password for remote console access" );
2704 	cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP );
2705 
2706 	cl_timedemo = Cvar_Get ("timedemo", "0", 0);
2707 	cl_aviFrameRate = Cvar_Get ("cl_aviFrameRate", "25", CVAR_ARCHIVE);
2708 	cl_aviMotionJpeg = Cvar_Get ("cl_aviMotionJpeg", "1", CVAR_ARCHIVE);
2709 	cl_avi2GBLimit = Cvar_Get ("cl_avi2GBLimit", "1", CVAR_ARCHIVE );
2710 	cl_forceavidemo = Cvar_Get ("cl_forceavidemo", "0", 0);
2711 
2712 	rconAddress = Cvar_Get ("rconAddress", "", 0, "Alternate server address to remotely access via rcon protocol");
2713 
2714 	cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE_ND );
2715 	cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE_ND );
2716 	cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", CVAR_ARCHIVE_ND );
2717 
2718 	cl_maxpackets = Cvar_Get ("cl_maxpackets", "63", CVAR_ARCHIVE );
2719 	cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE_ND );
2720 
2721 	cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE_ND, "Always run");
2722 	cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE, "Mouse sensitivity value");
2723 	cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE_ND, "Mouse acceleration value");
2724 	cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE_ND, "Mouse look" );
2725 
2726 	// 0: legacy mouse acceleration
2727 	// 1: new implementation
2728 	cl_mouseAccelStyle = Cvar_Get( "cl_mouseAccelStyle", "0", CVAR_ARCHIVE_ND, "Mouse accelration style (0:legacy, 1:QuakeLive)" );
2729 	// offset for the power function (for style 1, ignored otherwise)
2730 	// this should be set to the max rate value
2731 	cl_mouseAccelOffset = Cvar_Get( "cl_mouseAccelOffset", "5", CVAR_ARCHIVE_ND, "Mouse acceleration offset for style 1" );
2732 
2733 	cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0);
2734 	cl_framerate	= Cvar_Get ("cl_framerate", "0", CVAR_TEMP);
2735 	cl_allowDownload = Cvar_Get ("cl_allowDownload", "0", CVAR_ARCHIVE_ND, "Allow downloading custom paks from server");
2736 	cl_allowAltEnter = Cvar_Get ("cl_allowAltEnter", "1", CVAR_ARCHIVE_ND, "Enables use of ALT+ENTER keyboard combo to toggle fullscreen" );
2737 
2738 	cl_autolodscale = Cvar_Get( "cl_autolodscale", "1", CVAR_ARCHIVE_ND );
2739 
2740 	cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0);
2741 	cl_inGameVideo = Cvar_Get ("r_inGameVideo", "1", CVAR_ARCHIVE_ND );
2742 
2743 	cl_serverStatusResendTime = Cvar_Get ("cl_serverStatusResendTime", "750", 0);
2744 
2745 	// init autoswitch so the ui will have it correctly even
2746 	// if the cgame hasn't been started
2747 	Cvar_Get ("cg_autoswitch", "1", CVAR_ARCHIVE);
2748 
2749 	m_pitchVeh = Cvar_Get ("m_pitchVeh", "0.022", CVAR_ARCHIVE_ND);
2750 	m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE_ND);
2751 	m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE_ND);
2752 	m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE_ND);
2753 	m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE_ND);
2754 #ifdef MACOS_X
2755         // Input is jittery on OS X w/o this
2756 	m_filter = Cvar_Get ("m_filter", "1", CVAR_ARCHIVE_ND);
2757 #else
2758 	m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE_ND);
2759 #endif
2760 
2761 	cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM );
2762 
2763 	Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE_ND, "Max. ping for servers when searching the serverlist" );
2764 
2765 	cl_lanForcePackets = Cvar_Get ("cl_lanForcePackets", "1", CVAR_ARCHIVE_ND);
2766 
2767 	cl_drawRecording = Cvar_Get("cl_drawRecording", "1", CVAR_ARCHIVE);
2768 
2769 	// enable the ja_guid player identifier in userinfo by default in OpenJK
2770 	cl_enableGuid = Cvar_Get("cl_enableGuid", "1", CVAR_ARCHIVE_ND, "Enable GUID userinfo identifier" );
2771 	cl_guidServerUniq = Cvar_Get ("cl_guidServerUniq", "1", CVAR_ARCHIVE_ND, "Use a unique guid value per server" );
2772 
2773 	// ~ and `, as keys and characters
2774 	cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "~ ` 0x7e 0x60 0xb2", CVAR_ARCHIVE, "Which keys are used to toggle the console");
2775 	cl_consoleUseScanCode = Cvar_Get( "cl_consoleUseScanCode", "1", CVAR_ARCHIVE, "Use native console key detection" );
2776 
2777 	// userinfo
2778 	Cvar_Get ("name", "Padawan", CVAR_USERINFO | CVAR_ARCHIVE_ND, "Player name" );
2779 	Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE, "Data rate" );
2780 	Cvar_Get ("snaps", "40", CVAR_USERINFO | CVAR_ARCHIVE, "Client snapshots per second" );
2781 	Cvar_Get ("model", DEFAULT_MODEL"/default", CVAR_USERINFO | CVAR_ARCHIVE, "Player model" );
2782 	Cvar_Get ("forcepowers", "7-1-032330000000001333", CVAR_USERINFO | CVAR_ARCHIVE, "Player forcepowers" );
2783 //	Cvar_Get ("g_redTeam", DEFAULT_REDTEAM_NAME, CVAR_SERVERINFO | CVAR_ARCHIVE);
2784 //	Cvar_Get ("g_blueTeam", DEFAULT_BLUETEAM_NAME, CVAR_SERVERINFO | CVAR_ARCHIVE);
2785 	Cvar_Get ("color1",  "4", CVAR_USERINFO | CVAR_ARCHIVE, "Player saber1 color" );
2786 	Cvar_Get ("color2", "4", CVAR_USERINFO | CVAR_ARCHIVE, "Player saber2 color" );
2787 	Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE, "Player handicap" );
2788 	Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE, "Player sex" );
2789 	Cvar_Get ("password", "", CVAR_USERINFO, "Password to join server" );
2790 	Cvar_Get ("cg_predictItems", "1", CVAR_USERINFO | CVAR_ARCHIVE );
2791 
2792 	//default sabers
2793 	Cvar_Get ("saber1",  DEFAULT_SABER, CVAR_USERINFO | CVAR_ARCHIVE, "Player default right hand saber" );
2794 	Cvar_Get ("saber2",  "none", CVAR_USERINFO | CVAR_ARCHIVE, "Player left hand saber" );
2795 
2796 	//skin color
2797 	Cvar_Get ("char_color_red",  "255", CVAR_USERINFO | CVAR_ARCHIVE, "Player tint (Red)" );
2798 	Cvar_Get ("char_color_green",  "255", CVAR_USERINFO | CVAR_ARCHIVE, "Player tint (Green)" );
2799 	Cvar_Get ("char_color_blue",  "255", CVAR_USERINFO | CVAR_ARCHIVE, "Player tint (Blue)" );
2800 
2801 	// cgame might not be initialized before menu is used
2802 	Cvar_Get ("cg_viewsize", "100", CVAR_ARCHIVE_ND );
2803 
2804 	//
2805 	// register our commands
2806 	//
2807 	Cmd_AddCommand ("cmd", CL_ForwardToServer_f, "Forward command to server" );
2808 	Cmd_AddCommand ("globalservers", CL_GlobalServers_f, "Query the masterserver for serverlist" );
2809 	Cmd_AddCommand( "addFavorite", CL_AddFavorite_f, "Add server to favorites" );
2810 	Cmd_AddCommand ("record", CL_Record_f, "Record a demo" );
2811 	Cmd_AddCommand ("demo", CL_PlayDemo_f, "Playback a demo" );
2812 	Cmd_SetCommandCompletionFunc( "demo", CL_CompleteDemoName );
2813 	Cmd_AddCommand ("stoprecord", CL_StopRecord_f, "Stop recording a demo" );
2814 	Cmd_AddCommand ("configstrings", CL_Configstrings_f, "Prints the configstrings list" );
2815 	Cmd_AddCommand ("clientinfo", CL_Clientinfo_f, "Prints the userinfo variables" );
2816 	Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f, "Restart sound" );
2817 	Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f, "Restart the renderer - or change the resolution" );
2818 	Cmd_AddCommand ("disconnect", CL_Disconnect_f, "Disconnect from current server" );
2819 	Cmd_AddCommand ("cinematic", CL_PlayCinematic_f, "Play a cinematic video" );
2820 	Cmd_AddCommand ("connect", CL_Connect_f, "Connect to a server" );
2821 	Cmd_AddCommand ("reconnect", CL_Reconnect_f, "Reconnect to current server" );
2822 	Cmd_AddCommand ("localservers", CL_LocalServers_f, "Query LAN for local servers" );
2823 	Cmd_AddCommand ("rcon", CL_Rcon_f, "Execute commands remotely to a server" );
2824 	Cmd_SetCommandCompletionFunc( "rcon", CL_CompleteRcon );
2825 	Cmd_AddCommand ("ping", CL_Ping_f, "Ping a server for info response" );
2826 	Cmd_AddCommand ("serverstatus", CL_ServerStatus_f, "Retrieve current or specified server's status" );
2827 	Cmd_AddCommand ("showip", CL_ShowIP_f, "Shows local IP" );
2828 	Cmd_AddCommand ("fs_openedList", CL_OpenedPK3List_f, "Lists open pak files" );
2829 	Cmd_AddCommand ("fs_referencedList", CL_ReferencedPK3List_f, "Lists referenced pak files" );
2830 	Cmd_AddCommand ("model", CL_SetModel_f, "Set the player model" );
2831 	Cmd_AddCommand ("forcepowers", CL_SetForcePowers_f );
2832 	Cmd_AddCommand ("video", CL_Video_f, "Record demo to avi" );
2833 	Cmd_AddCommand ("stopvideo", CL_StopVideo_f, "Stop avi recording" );
2834 
2835 	CL_InitRef();
2836 
2837 	SCR_Init ();
2838 
2839 	Cbuf_Execute ();
2840 
2841 	Cvar_Set( "cl_running", "1" );
2842 
2843 	G2VertSpaceClient = new CMiniHeap (G2_VERT_SPACE_CLIENT_SIZE * 1024);
2844 
2845 	CL_GenerateQKey();
2846 	CL_UpdateGUID( NULL, 0 );
2847 
2848 //	Com_Printf( "----- Client Initialization Complete -----\n" );
2849 }
2850 
2851 
2852 /*
2853 ===============
2854 CL_Shutdown
2855 
2856 ===============
2857 */
CL_Shutdown(void)2858 void CL_Shutdown( void ) {
2859 	static qboolean recursive = qfalse;
2860 
2861 	//Com_Printf( "----- CL_Shutdown -----\n" );
2862 
2863 	if ( recursive ) {
2864 		Com_Printf ("WARNING: Recursive CL_Shutdown called!\n");
2865 		return;
2866 	}
2867 	recursive = qtrue;
2868 
2869 	if (G2VertSpaceClient)
2870 	{
2871 		delete G2VertSpaceClient;
2872 		G2VertSpaceClient = 0;
2873 	}
2874 
2875 	CL_Disconnect( qtrue );
2876 
2877 	// RJ: added the shutdown all to close down the cgame (to free up some memory, such as in the fx system)
2878 	CL_ShutdownAll( qtrue );
2879 
2880 	S_Shutdown();
2881 	//CL_ShutdownUI();
2882 
2883 	Cmd_RemoveCommand ("cmd");
2884 	Cmd_RemoveCommand ("configstrings");
2885 	Cmd_RemoveCommand ("clientinfo");
2886 	Cmd_RemoveCommand ("snd_restart");
2887 	Cmd_RemoveCommand ("vid_restart");
2888 	Cmd_RemoveCommand ("disconnect");
2889 	Cmd_RemoveCommand ("record");
2890 	Cmd_RemoveCommand ("demo");
2891 	Cmd_RemoveCommand ("cinematic");
2892 	Cmd_RemoveCommand ("stoprecord");
2893 	Cmd_RemoveCommand ("connect");
2894 	Cmd_RemoveCommand ("reconnect");
2895 	Cmd_RemoveCommand ("localservers");
2896 	Cmd_RemoveCommand ("globalservers");
2897 	Cmd_RemoveCommand( "addFavorite" );
2898 	Cmd_RemoveCommand ("rcon");
2899 	Cmd_RemoveCommand ("ping");
2900 	Cmd_RemoveCommand ("serverstatus");
2901 	Cmd_RemoveCommand ("showip");
2902 	Cmd_RemoveCommand ("fs_openedList");
2903 	Cmd_RemoveCommand ("fs_referencedList");
2904 	Cmd_RemoveCommand ("model");
2905 	Cmd_RemoveCommand ("forcepowers");
2906 	Cmd_RemoveCommand ("video");
2907 	Cmd_RemoveCommand ("stopvideo");
2908 
2909 	CL_ShutdownInput();
2910 	Con_Shutdown();
2911 
2912 	Cvar_Set( "cl_running", "0" );
2913 
2914 	recursive = qfalse;
2915 
2916 	Com_Memset( &cls, 0, sizeof( cls ) );
2917 	Key_SetCatcher( 0 );
2918 
2919 	//Com_Printf( "-----------------------\n" );
2920 
2921 }
2922 
CL_ConnectedToRemoteServer(void)2923 qboolean CL_ConnectedToRemoteServer( void ) {
2924 	return (qboolean)( com_sv_running && !com_sv_running->integer && cls.state >= CA_CONNECTED && !clc.demoplaying );
2925 }
2926 
CL_SetServerInfo(serverInfo_t * server,const char * info,int ping)2927 static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) {
2928 	if (server) {
2929 		if (info) {
2930 			server->clients = atoi(Info_ValueForKey(info, "clients"));
2931 			Q_strncpyz(server->hostName,Info_ValueForKey(info, "hostname"), MAX_NAME_LENGTH);
2932 			Q_strncpyz(server->mapName, Info_ValueForKey(info, "mapname"), MAX_NAME_LENGTH);
2933 			server->maxClients = atoi(Info_ValueForKey(info, "sv_maxclients"));
2934 			Q_strncpyz(server->game,Info_ValueForKey(info, "game"), MAX_NAME_LENGTH);
2935 			server->gameType = atoi(Info_ValueForKey(info, "gametype"));
2936 			server->netType = atoi(Info_ValueForKey(info, "nettype"));
2937 			server->minPing = atoi(Info_ValueForKey(info, "minping"));
2938 			server->maxPing = atoi(Info_ValueForKey(info, "maxping"));
2939 //			server->allowAnonymous = atoi(Info_ValueForKey(info, "sv_allowAnonymous"));
2940 			server->needPassword = (qboolean)atoi(Info_ValueForKey(info, "needpass" ));
2941 			server->trueJedi = atoi(Info_ValueForKey(info, "truejedi" ));
2942 			server->weaponDisable = atoi(Info_ValueForKey(info, "wdisable" ));
2943 			server->forceDisable = atoi(Info_ValueForKey(info, "fdisable" ));
2944 			server->humans = atoi( Info_ValueForKey( info, "g_humanplayers" ) );
2945 			server->bots = atoi( Info_ValueForKey( info, "bots" ) );
2946 //			server->pure = (qboolean)atoi(Info_ValueForKey(info, "pure" ));
2947 		}
2948 		server->ping = ping;
2949 	}
2950 }
2951 
CL_SetServerInfoByAddress(netadr_t from,const char * info,int ping)2952 static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping) {
2953 	int i;
2954 
2955 	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
2956 		if (NET_CompareAdr(from, cls.localServers[i].adr)) {
2957 			CL_SetServerInfo(&cls.localServers[i], info, ping);
2958 		}
2959 	}
2960 
2961 	for (i = 0; i < MAX_GLOBAL_SERVERS; i++) {
2962 		if (NET_CompareAdr(from, cls.globalServers[i].adr)) {
2963 			CL_SetServerInfo(&cls.globalServers[i], info, ping);
2964 		}
2965 	}
2966 
2967 	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
2968 		if (NET_CompareAdr(from, cls.favoriteServers[i].adr)) {
2969 			CL_SetServerInfo(&cls.favoriteServers[i], info, ping);
2970 		}
2971 	}
2972 }
2973 
2974 /*
2975 ===================
2976 CL_ServerInfoPacket
2977 ===================
2978 */
CL_ServerInfoPacket(netadr_t from,msg_t * msg)2979 void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
2980 	int		i, type;
2981 	char	info[MAX_INFO_STRING];
2982 	char	*infoString;
2983 	int		prot;
2984 
2985 	infoString = MSG_ReadString( msg );
2986 
2987 	// if this isn't the correct protocol version, ignore it
2988 	prot = atoi( Info_ValueForKey( infoString, "protocol" ) );
2989 	if ( prot != PROTOCOL_VERSION ) {
2990 		Com_DPrintf( "Different protocol info packet: %s\n", infoString );
2991 		return;
2992 	}
2993 
2994 	// iterate servers waiting for ping response
2995 	for (i=0; i<MAX_PINGREQUESTS; i++)
2996 	{
2997 		if ( cl_pinglist[i].adr.port && !cl_pinglist[i].time && NET_CompareAdr( from, cl_pinglist[i].adr ) )
2998 		{
2999 			// calc ping time
3000 			cl_pinglist[i].time = Sys_Milliseconds() - cl_pinglist[i].start;
3001 			if ( com_developer->integer ) {
3002 				Com_Printf( "ping time %dms from %s\n", cl_pinglist[i].time, NET_AdrToString( from ) );
3003 			}
3004 
3005 			// save of info
3006 			Q_strncpyz( cl_pinglist[i].info, infoString, sizeof( cl_pinglist[i].info ) );
3007 
3008 			// tack on the net type
3009 			// NOTE: make sure these types are in sync with the netnames strings in the UI
3010 			switch (from.type)
3011 			{
3012 				case NA_BROADCAST:
3013 				case NA_IP:
3014 					type = 1;
3015 					break;
3016 
3017 				default:
3018 					type = 0;
3019 					break;
3020 			}
3021 			Info_SetValueForKey( cl_pinglist[i].info, "nettype", va("%d", type) );
3022 			CL_SetServerInfoByAddress(from, infoString, cl_pinglist[i].time);
3023 
3024 			return;
3025 		}
3026 	}
3027 
3028 	// if not just sent a local broadcast or pinging local servers
3029 	if (cls.pingUpdateSource != AS_LOCAL) {
3030 		return;
3031 	}
3032 
3033 	for ( i = 0 ; i < MAX_OTHER_SERVERS ; i++ ) {
3034 		// empty slot
3035 		if ( cls.localServers[i].adr.port == 0 ) {
3036 			break;
3037 		}
3038 
3039 		// avoid duplicate
3040 		if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) {
3041 			return;
3042 		}
3043 	}
3044 
3045 	if ( i == MAX_OTHER_SERVERS ) {
3046 		Com_DPrintf( "MAX_OTHER_SERVERS hit, dropping infoResponse\n" );
3047 		return;
3048 	}
3049 
3050 	// add this to the list
3051 	cls.numlocalservers = i+1;
3052 	CL_InitServerInfo( &cls.localServers[i], &from );
3053 
3054 	Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING );
3055 	if (strlen(info)) {
3056 		if (info[strlen(info)-1] != '\n') {
3057 			Q_strcat(info, sizeof(info), "\n");
3058 		}
3059 		Com_Printf( "%s: %s", NET_AdrToString( from ), info );
3060 	}
3061 }
3062 
3063 /*
3064 ===================
3065 CL_GetServerStatus
3066 ===================
3067 */
CL_GetServerStatus(netadr_t from)3068 serverStatus_t *CL_GetServerStatus( netadr_t from ) {
3069 	int i, oldest, oldestTime;
3070 
3071 	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3072 		if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
3073 			return &cl_serverStatusList[i];
3074 		}
3075 	}
3076 	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3077 		if ( cl_serverStatusList[i].retrieved ) {
3078 			return &cl_serverStatusList[i];
3079 		}
3080 	}
3081 	oldest = -1;
3082 	oldestTime = 0;
3083 	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3084 		if (oldest == -1 || cl_serverStatusList[i].startTime < oldestTime) {
3085 			oldest = i;
3086 			oldestTime = cl_serverStatusList[i].startTime;
3087 		}
3088 	}
3089 	if (oldest != -1) {
3090 		return &cl_serverStatusList[oldest];
3091 	}
3092 	serverStatusCount++;
3093 	return &cl_serverStatusList[serverStatusCount & (MAX_SERVERSTATUSREQUESTS-1)];
3094 }
3095 
3096 /*
3097 ===================
3098 CL_ServerStatus
3099 ===================
3100 */
CL_ServerStatus(const char * serverAddress,char * serverStatusString,int maxLen)3101 int CL_ServerStatus( const char *serverAddress, char *serverStatusString, int maxLen ) {
3102 	int i;
3103 	netadr_t	to;
3104 	serverStatus_t *serverStatus;
3105 
3106 	// if no server address then reset all server status requests
3107 	if ( !serverAddress ) {
3108 		for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3109 			cl_serverStatusList[i].address.port = 0;
3110 			cl_serverStatusList[i].retrieved = qtrue;
3111 		}
3112 		return qfalse;
3113 	}
3114 	// get the address
3115 	if ( !NET_StringToAdr( serverAddress, &to ) ) {
3116 		return qfalse;
3117 	}
3118 	serverStatus = CL_GetServerStatus( to );
3119 	// if no server status string then reset the server status request for this address
3120 	if ( !serverStatusString ) {
3121 		serverStatus->retrieved = qtrue;
3122 		return qfalse;
3123 	}
3124 
3125 	// if this server status request has the same address
3126 	if ( NET_CompareAdr( to, serverStatus->address) ) {
3127 		// if we received a response for this server status request
3128 		if (!serverStatus->pending) {
3129 			Q_strncpyz(serverStatusString, serverStatus->string, maxLen);
3130 			serverStatus->retrieved = qtrue;
3131 			serverStatus->startTime = 0;
3132 			return qtrue;
3133 		}
3134 		// resend the request regularly
3135 		else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) {
3136 			serverStatus->print = qfalse;
3137 			serverStatus->pending = qtrue;
3138 			serverStatus->retrieved = qfalse;
3139 			serverStatus->time = 0;
3140 			serverStatus->startTime = Com_Milliseconds();
3141 			NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
3142 			return qfalse;
3143 		}
3144 	}
3145 	// if retrieved
3146 	else if ( serverStatus->retrieved ) {
3147 		serverStatus->address = to;
3148 		serverStatus->print = qfalse;
3149 		serverStatus->pending = qtrue;
3150 		serverStatus->retrieved = qfalse;
3151 		serverStatus->startTime = Com_Milliseconds();
3152 		serverStatus->time = 0;
3153 		NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
3154 		return qfalse;
3155 	}
3156 	return qfalse;
3157 }
3158 
3159 /*
3160 ===================
3161 CL_ServerStatusResponse
3162 ===================
3163 */
CL_ServerStatusResponse(netadr_t from,msg_t * msg)3164 void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) {
3165 	char	*s;
3166 	char	info[MAX_INFO_STRING];
3167 	int		i, l, score, ping;
3168 	int		len;
3169 	serverStatus_t *serverStatus;
3170 
3171 	serverStatus = NULL;
3172 	for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
3173 		if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
3174 			serverStatus = &cl_serverStatusList[i];
3175 			break;
3176 		}
3177 	}
3178 	// if we didn't request this server status
3179 	if (!serverStatus) {
3180 		return;
3181 	}
3182 
3183 	s = MSG_ReadStringLine( msg );
3184 
3185 	len = 0;
3186 	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "%s", s);
3187 
3188 	if (serverStatus->print) {
3189 		Com_Printf( "Server (%s)\n",
3190 			NET_AdrToString( serverStatus->address ) );
3191 		Com_Printf("Server settings:\n");
3192 		// print cvars
3193 		while (*s) {
3194 			for (i = 0; i < 2 && *s; i++) {
3195 				if (*s == '\\')
3196 					s++;
3197 				l = 0;
3198 				while (*s) {
3199 					info[l++] = *s;
3200 					if (l >= MAX_INFO_STRING-1)
3201 						break;
3202 					s++;
3203 					if (*s == '\\') {
3204 						break;
3205 					}
3206 				}
3207 				info[l] = '\0';
3208 				if (i) {
3209 					Com_Printf("%s\n", info);
3210 				}
3211 				else {
3212 					Com_Printf("%-24s", info);
3213 				}
3214 			}
3215 		}
3216 	}
3217 
3218 	len = strlen(serverStatus->string);
3219 	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
3220 
3221 	if (serverStatus->print) {
3222 		Com_Printf("\nPlayers:\n");
3223 		Com_Printf("num: score: ping: name:\n");
3224 	}
3225 	for (i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++) {
3226 
3227 		len = strlen(serverStatus->string);
3228 		Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\%s", s);
3229 
3230 		if (serverStatus->print) {
3231 			score = ping = 0;
3232 			sscanf(s, "%d %d", &score, &ping);
3233 			s = strchr(s, ' ');
3234 			if (s)
3235 				s = strchr(s+1, ' ');
3236 			if (s)
3237 				s++;
3238 			else
3239 				s = "unknown";
3240 			Com_Printf("%-2d   %-3d    %-3d   %s\n", i, score, ping, s );
3241 		}
3242 	}
3243 	len = strlen(serverStatus->string);
3244 	Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
3245 
3246 	serverStatus->time = Com_Milliseconds();
3247 	serverStatus->address = from;
3248 	serverStatus->pending = qfalse;
3249 	if (serverStatus->print) {
3250 		serverStatus->retrieved = qtrue;
3251 	}
3252 }
3253 
3254 /*
3255 ==================
3256 CL_LocalServers_f
3257 ==================
3258 */
CL_LocalServers_f(void)3259 void CL_LocalServers_f( void ) {
3260 	char		*message;
3261 	int			i, j;
3262 	netadr_t	to;
3263 
3264 	Com_Printf( "Scanning for servers on the local network...\n");
3265 
3266 	// reset the list, waiting for response
3267 	cls.numlocalservers = 0;
3268 	cls.pingUpdateSource = AS_LOCAL;
3269 
3270 	for (i = 0; i < MAX_OTHER_SERVERS; i++) {
3271 		qboolean b = cls.localServers[i].visible;
3272 		Com_Memset(&cls.localServers[i], 0, sizeof(cls.localServers[i]));
3273 		cls.localServers[i].visible = b;
3274 	}
3275 	Com_Memset( &to, 0, sizeof( to ) );
3276 
3277 	// The 'xxx' in the message is a challenge that will be echoed back
3278 	// by the server.  We don't care about that here, but master servers
3279 	// can use that to prevent spoofed server responses from invalid ip
3280 	message = "\377\377\377\377getinfo xxx";
3281 
3282 	// send each message twice in case one is dropped
3283 	for ( i = 0 ; i < 2 ; i++ ) {
3284 		// send a broadcast packet on each server port
3285 		// we support multiple server ports so a single machine
3286 		// can nicely run multiple servers
3287 		for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) {
3288 			to.port = BigShort( (short)(PORT_SERVER + j) );
3289 
3290 			to.type = NA_BROADCAST;
3291 			NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
3292 		}
3293 	}
3294 }
3295 
3296 /*
3297 ==================
3298 CL_GlobalServers_f
3299 
3300 Originally master 0 was Internet and master 1 was MPlayer.
3301 ioquake3 2008; added support for requesting five separate master servers using 0-4.
3302 ioquake3 2017; made master 0 fetch all master servers and 1-5 request a single master server.
3303 OpenJK 2013; added support for requesting five separate master servers using 0-4.
3304 OpenJK July 2017; made master 0 fetch all master servers and 1-5 request a single master server.
3305 
3306 ==================
3307 */
CL_GlobalServers_f(void)3308 void CL_GlobalServers_f( void ) {
3309 	netadr_t	to;
3310 	int			count, i, masterNum;
3311 	char		command[1024], *masteraddress;
3312 
3313 	if ((count = Cmd_Argc()) < 3 || (masterNum = atoi(Cmd_Argv(1))) < 0 || masterNum > MAX_MASTER_SERVERS)
3314 	{
3315 		Com_Printf("usage: globalservers <master# 0-%d> <protocol> [keywords]\n", MAX_MASTER_SERVERS);
3316 		return;
3317 	}
3318 
3319 	// request from all master servers
3320 	if ( masterNum == 0 ) {
3321 		int numAddress = 0;
3322 
3323 		for ( i = 1; i <= MAX_MASTER_SERVERS; i++ ) {
3324 			Com_sprintf( command, sizeof(command), "sv_master%d", i );
3325 			masteraddress = Cvar_VariableString(command);
3326 
3327 			if(!*masteraddress)
3328 				continue;
3329 
3330 			numAddress++;
3331 
3332 			Com_sprintf(command, sizeof(command), "globalservers %d %s %s\n", i, Cmd_Argv(2), Cmd_ArgsFrom(3));
3333 			Cbuf_AddText(command);
3334 		}
3335 
3336 		if ( !numAddress ) {
3337 			Com_Printf( "CL_GlobalServers_f: Error: No master server addresses.\n");
3338 		}
3339 		return;
3340 	}
3341 
3342 	Com_sprintf( command, sizeof(command), "sv_master%d", masterNum );
3343 	masteraddress = Cvar_VariableString( command );
3344 
3345 	if ( !*masteraddress )
3346 	{
3347 		Com_Printf( "CL_GlobalServers_f: Error: No master server address given for %s.\n", command );
3348 		return;
3349 	}
3350 
3351 	// reset the list, waiting for response
3352 	// -1 is used to distinguish a "no response"
3353 
3354 	i = NET_StringToAdr( masteraddress, &to );
3355 
3356 	if (!i)
3357 	{
3358 		Com_Printf( "CL_GlobalServers_f: Error: could not resolve address of master %s\n", masteraddress );
3359 		return;
3360 	}
3361 	to.type = NA_IP;
3362 	to.port = BigShort(PORT_MASTER);
3363 
3364 	Com_Printf( "Requesting servers from the master %s (%s)...\n", masteraddress, NET_AdrToString( to ) );
3365 
3366 	cls.numglobalservers = -1;
3367 	cls.pingUpdateSource = AS_GLOBAL;
3368 
3369 	Com_sprintf(command, sizeof(command), "getservers %s", Cmd_Argv(2));
3370 
3371 	// tack on keywords
3372 	for (i = 3; i < count; i++)
3373 	{
3374 		Q_strcat(command, sizeof(command), " ");
3375 		Q_strcat(command, sizeof(command), Cmd_Argv(i));
3376 	}
3377 
3378 	NET_OutOfBandPrint( NS_SERVER, to, "%s", command );
3379 }
3380 
3381 /*
3382 ==================
3383 CL_GetPing
3384 ==================
3385 */
CL_GetPing(int n,char * buf,int buflen,int * pingtime)3386 void CL_GetPing( int n, char *buf, int buflen, int *pingtime )
3387 {
3388 	const char	*str;
3389 	int		time;
3390 	int		maxPing;
3391 
3392 	if (n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port)
3393 	{
3394 		// empty or invalid slot
3395 		buf[0]    = '\0';
3396 		*pingtime = 0;
3397 		return;
3398 	}
3399 
3400 	str = NET_AdrToString( cl_pinglist[n].adr );
3401 	Q_strncpyz( buf, str, buflen );
3402 
3403 	time = cl_pinglist[n].time;
3404 	if (!time)
3405 	{
3406 		// check for timeout
3407 		time = Sys_Milliseconds() - cl_pinglist[n].start;
3408 		maxPing = Cvar_VariableIntegerValue( "cl_maxPing" );
3409 		if( maxPing < 100 ) {
3410 			maxPing = 100;
3411 		}
3412 		if (time < maxPing)
3413 		{
3414 			// not timed out yet
3415 			time = 0;
3416 		}
3417 	}
3418 
3419 	CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time);
3420 
3421 	*pingtime = time;
3422 }
3423 
3424 /*
3425 ==================
3426 CL_GetPingInfo
3427 ==================
3428 */
CL_GetPingInfo(int n,char * buf,int buflen)3429 void CL_GetPingInfo( int n, char *buf, int buflen )
3430 {
3431 	if (n < 0 || n >= MAX_PINGREQUESTS || !cl_pinglist[n].adr.port)
3432 	{
3433 		// empty or invalid slot
3434 		if (buflen)
3435 			buf[0] = '\0';
3436 		return;
3437 	}
3438 
3439 	Q_strncpyz( buf, cl_pinglist[n].info, buflen );
3440 }
3441 
3442 /*
3443 ==================
3444 CL_ClearPing
3445 ==================
3446 */
CL_ClearPing(int n)3447 void CL_ClearPing( int n )
3448 {
3449 	if (n < 0 || n >= MAX_PINGREQUESTS)
3450 		return;
3451 
3452 	cl_pinglist[n].adr.port = 0;
3453 }
3454 
3455 /*
3456 ==================
3457 CL_GetPingQueueCount
3458 ==================
3459 */
CL_GetPingQueueCount(void)3460 int CL_GetPingQueueCount( void )
3461 {
3462 	int		i;
3463 	int		count;
3464 	ping_t*	pingptr;
3465 
3466 	count   = 0;
3467 	pingptr = cl_pinglist;
3468 
3469 	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ ) {
3470 		if (pingptr->adr.port) {
3471 			count++;
3472 		}
3473 	}
3474 
3475 	return (count);
3476 }
3477 
3478 /*
3479 ==================
3480 CL_GetFreePing
3481 ==================
3482 */
CL_GetFreePing(void)3483 ping_t* CL_GetFreePing( void )
3484 {
3485 	ping_t*	pingptr;
3486 	ping_t*	best;
3487 	int		oldest;
3488 	int		i;
3489 	int		time;
3490 
3491 	pingptr = cl_pinglist;
3492 	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
3493 	{
3494 		// find free ping slot
3495 		if (pingptr->adr.port)
3496 		{
3497 			if (!pingptr->time)
3498 			{
3499 				if (Sys_Milliseconds() - pingptr->start < 500)
3500 				{
3501 					// still waiting for response
3502 					continue;
3503 				}
3504 			}
3505 			else if (pingptr->time < 500)
3506 			{
3507 				// results have not been queried
3508 				continue;
3509 			}
3510 		}
3511 
3512 		// clear it
3513 		pingptr->adr.port = 0;
3514 		return (pingptr);
3515 	}
3516 
3517 	// use oldest entry
3518 	pingptr = cl_pinglist;
3519 	best    = cl_pinglist;
3520 	oldest  = INT_MIN;
3521 	for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
3522 	{
3523 		// scan for oldest
3524 		time = Sys_Milliseconds() - pingptr->start;
3525 		if (time > oldest)
3526 		{
3527 			oldest = time;
3528 			best   = pingptr;
3529 		}
3530 	}
3531 
3532 	return (best);
3533 }
3534 
3535 /*
3536 ==================
3537 CL_Ping_f
3538 ==================
3539 */
CL_Ping_f(void)3540 void CL_Ping_f( void ) {
3541 	netadr_t	to;
3542 	ping_t*		pingptr;
3543 	char*		server;
3544 
3545 	if ( Cmd_Argc() != 2 ) {
3546 		Com_Printf( "usage: ping [server]\n");
3547 		return;
3548 	}
3549 
3550 	Com_Memset( &to, 0, sizeof(netadr_t) );
3551 
3552 	server = Cmd_Argv(1);
3553 
3554 	if ( !NET_StringToAdr( server, &to ) ) {
3555 		return;
3556 	}
3557 
3558 	pingptr = CL_GetFreePing();
3559 
3560 	memcpy( &pingptr->adr, &to, sizeof (netadr_t) );
3561 	pingptr->start = Sys_Milliseconds();
3562 	pingptr->time  = 0;
3563 
3564 	CL_SetServerInfoByAddress(pingptr->adr, NULL, 0);
3565 
3566 	NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" );
3567 }
3568 
3569 /*
3570 ==================
3571 CL_UpdateVisiblePings_f
3572 ==================
3573 */
CL_UpdateVisiblePings_f(int source)3574 qboolean CL_UpdateVisiblePings_f(int source) {
3575 	int			slots, i;
3576 	char		buff[MAX_STRING_CHARS];
3577 	int			pingTime;
3578 	int			max;
3579 	qboolean status = qfalse;
3580 
3581 	if (source < 0 || source > AS_FAVORITES) {
3582 		return qfalse;
3583 	}
3584 
3585 	cls.pingUpdateSource = source;
3586 
3587 	slots = CL_GetPingQueueCount();
3588 	if (slots < MAX_PINGREQUESTS) {
3589 		serverInfo_t *server = NULL;
3590 
3591 		switch (source) {
3592 			case AS_LOCAL :
3593 				server = &cls.localServers[0];
3594 				max = cls.numlocalservers;
3595 			break;
3596 			case AS_GLOBAL :
3597 				server = &cls.globalServers[0];
3598 				max = cls.numglobalservers;
3599 			break;
3600 			case AS_FAVORITES :
3601 				server = &cls.favoriteServers[0];
3602 				max = cls.numfavoriteservers;
3603 			break;
3604 			default:
3605 				return qfalse;
3606 		}
3607 		for (i = 0; i < max; i++) {
3608 			if (server[i].visible) {
3609 				if (server[i].ping == -1) {
3610 					int j;
3611 
3612 					if (slots >= MAX_PINGREQUESTS) {
3613 						break;
3614 					}
3615 					for (j = 0; j < MAX_PINGREQUESTS; j++) {
3616 						if (!cl_pinglist[j].adr.port) {
3617 							continue;
3618 						}
3619 						if (NET_CompareAdr( cl_pinglist[j].adr, server[i].adr)) {
3620 							// already on the list
3621 							break;
3622 						}
3623 					}
3624 					if (j >= MAX_PINGREQUESTS) {
3625 						status = qtrue;
3626 						for (j = 0; j < MAX_PINGREQUESTS; j++) {
3627 							if (!cl_pinglist[j].adr.port) {
3628 								break;
3629 							}
3630 						}
3631 						memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t));
3632 						cl_pinglist[j].start = Sys_Milliseconds();
3633 						cl_pinglist[j].time = 0;
3634 						NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" );
3635 						slots++;
3636 					}
3637 				}
3638 				// if the server has a ping higher than cl_maxPing or
3639 				// the ping packet got lost
3640 				else if (server[i].ping == 0) {
3641 					// if we are updating global servers
3642 					if (source == AS_GLOBAL) {
3643 						//
3644 						if ( cls.numGlobalServerAddresses > 0 ) {
3645 							// overwrite this server with one from the additional global servers
3646 							cls.numGlobalServerAddresses--;
3647 							CL_InitServerInfo(&server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses]);
3648 							// NOTE: the server[i].visible flag stays untouched
3649 						}
3650 					}
3651 				}
3652 			}
3653 		}
3654 	}
3655 
3656 	if (slots) {
3657 		status = qtrue;
3658 	}
3659 	for (i = 0; i < MAX_PINGREQUESTS; i++) {
3660 		if (!cl_pinglist[i].adr.port) {
3661 			continue;
3662 		}
3663 		CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime );
3664 		if (pingTime != 0) {
3665 			CL_ClearPing(i);
3666 			status = qtrue;
3667 		}
3668 	}
3669 
3670 	return status;
3671 }
3672 
3673 /*
3674 ==================
3675 CL_ServerStatus_f
3676 ==================
3677 */
CL_ServerStatus_f(void)3678 void CL_ServerStatus_f(void) {
3679 	netadr_t	to, *toptr = NULL;
3680 	char		*server;
3681 	serverStatus_t *serverStatus;
3682 
3683 	if ( Cmd_Argc() != 2 ) {
3684 		if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
3685 			Com_Printf ("Not connected to a server.\n");
3686 			Com_Printf( "Usage: serverstatus [server]\n");
3687 			return;
3688 		}
3689 
3690 		toptr = &clc.serverAddress;
3691 	}
3692 
3693 	if(!toptr)
3694 	{
3695 		Com_Memset( &to, 0, sizeof(netadr_t) );
3696 
3697 		server = Cmd_Argv(1);
3698 
3699 		toptr = &to;
3700 		if ( !NET_StringToAdr( server, toptr ) )
3701 			return;
3702 	}
3703 
3704 	NET_OutOfBandPrint( NS_CLIENT, *toptr, "getstatus" );
3705 
3706 	serverStatus = CL_GetServerStatus( *toptr );
3707 	serverStatus->address = *toptr;
3708 	serverStatus->print = qtrue;
3709 	serverStatus->pending = qtrue;
3710 }
3711 
3712 /*
3713 ==================
3714 CL_ShowIP_f
3715 ==================
3716 */
CL_ShowIP_f(void)3717 void CL_ShowIP_f(void) {
3718 	Sys_ShowIP();
3719 }
3720 
3721