1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19 
20 //
21 // cl_main.c
22 // Client main loop
23 //
24 
25 #include "cl_local.h"
26 
27 cVar_t *allow_download;
28 cVar_t *allow_download_players;
29 cVar_t *allow_download_models;
30 cVar_t *allow_download_sounds;
31 cVar_t *allow_download_maps;
32 
33 cVar_t	*cl_lightlevel;
34 cVar_t	*cl_downloadToBase;
35 cVar_t	*cl_maxfps;
36 cVar_t	*r_maxfps;
37 cVar_t	*cl_paused;
38 cVar_t	*cl_protocol;
39 
40 cVar_t	*cl_shownet;
41 
42 cVar_t	*cl_stereo_separation;
43 
44 cVar_t	*cl_timedemo;
45 cVar_t	*cl_timeout;
46 cVar_t	*cl_timestamp;
47 
48 cVar_t	*con_chatHud;
49 cVar_t	*con_chatHudLines;
50 cVar_t	*con_chatHudPosX;
51 cVar_t	*con_chatHudPosY;
52 cVar_t	*con_chatHudShadow;
53 cVar_t	*con_notifyfade;
54 cVar_t	*con_notifylarge;
55 cVar_t	*con_notifylines;
56 cVar_t	*con_notifytime;
57 cVar_t	*con_alpha;
58 cVar_t	*con_clock;
59 cVar_t	*con_drop;
60 cVar_t	*con_scroll;
61 
62 cVar_t	*freelook;
63 
64 cVar_t	*lookspring;
65 cVar_t	*lookstrafe;
66 
67 cVar_t	*m_pitch;
68 cVar_t	*m_yaw;
69 cVar_t	*m_forward;
70 cVar_t	*m_side;
71 
72 cVar_t	*rcon_clpassword;
73 cVar_t	*rcon_address;
74 
75 cVar_t	*r_fontScale;
76 
77 cVar_t	*sensitivity;
78 
79 cVar_t	*scr_conspeed;
80 
81 clientState_t	cl;
82 clientStatic_t	cls;
83 clMedia_t		clMedia;
84 
85 entityState_t	cl_baseLines[MAX_CS_EDICTS];
86 entityState_t	cl_parseEntities[MAX_PARSE_ENTITIES];
87 
88 static netAdr_t	cl_lastRConTo;
89 
90 struct memPool_s	*cl_cGameSysPool;
91 struct memPool_s	*cl_cinSysPool;
92 struct memPool_s	*cl_guiSysPool;
93 struct memPool_s	*cl_soundSysPool;
94 
95 //======================================================================
96 
97 /*
98 ===================
99 CL_ForwardCmdToServer
100 
101 Adds the current command line as a CLC_STRINGCMD to the client message.
102 things like godmode, noclip, etc, are commands directed to the server,
103 so when they are typed in at the console, they will need to be forwarded.
104 ===================
105 */
CL_ForwardCmdToServer(void)106 qBool CL_ForwardCmdToServer (void)
107 {
108 	char	*cmd;
109 
110 	cmd = Cmd_Argv (0);
111 	if (Com_ClientState () <= CA_CONNECTED || *cmd == '-' || *cmd == '+')
112 		return qFalse;
113 
114 	MSG_WriteByte (&cls.netChan.message, CLC_STRINGCMD);
115 	MSG_WriteStringCat (&cls.netChan.message, cmd);
116 	if (Cmd_Argc () > 1) {
117 		MSG_WriteStringCat (&cls.netChan.message, " ");
118 		MSG_WriteStringCat (&cls.netChan.message, Cmd_Args ());
119 	}
120 
121 	cls.forcePacket = qTrue;
122 	return qTrue;
123 }
124 
125 
126 /*
127 =======================
128 CL_SendConnectPacket
129 
130 We have gotten a challenge from the server, so try and connect.
131 ======================
132 */
CL_SendConnectPacket(int useProtocol)133 static void CL_SendConnectPacket (int useProtocol)
134 {
135 	netAdr_t	adr;
136 	int			port;
137 	uint32		msgLen;
138 
139 	if (!NET_StringToAdr (cls.serverName, &adr)) {
140 		Com_Printf (PRNT_WARNING, "Bad server address\n");
141 		cls.connectCount = 0;
142 		cls.connectTime = 0;
143 		return;
144 	}
145 
146 	if (!adr.port)
147 		adr.port = BigShort (PORT_SERVER);
148 
149 	port = Cvar_GetIntegerValue ("qport");
150 
151 	com_userInfoModified = qFalse;
152 
153 	// Decide on the maximum message length and protocol version to try
154 	if (Com_ServerState() == SS_DEMO) {
155 		msgLen = MAX_SV_USABLEMSG;
156 		cls.serverProtocol = ORIGINAL_PROTOCOL_VERSION;
157 	}
158 	else {
159 		if (!cls.serverProtocol) {
160 			if (cl_protocol->intVal) {
161 				// Force the version attempt
162 				cls.serverProtocol = cl_protocol->intVal;
163 				Com_Printf (PRNT_WARNING, "WARNING: CL_SendConnectPacket: cl_protocol is for debugging now, if you have trouble connecting set it to 0 to auto-detect (default).\n");
164 			}
165 			else if (useProtocol) {
166 				// Protocol version retrieved from server
167 				cls.serverProtocol = useProtocol;
168 			}
169 			else {
170 				// Really should never happen
171 				cls.serverProtocol = ENHANCED_PROTOCOL_VERSION;
172 			}
173 		}
174 
175 		// Enhanced protocol port is a byte
176 		if (cls.serverProtocol == ENHANCED_PROTOCOL_VERSION) {
177 			port &= 0xff;
178 
179 			msgLen = Cvar_GetIntegerValue ("net_maxMsgLen");
180 			if (msgLen > MAX_CL_USABLEMSG)
181 				msgLen = MAX_CL_USABLEMSG;
182 		}
183 		else {
184 			msgLen = MAX_SV_USABLEMSG;
185 		}
186 	}
187 
188 	cls.quakePort = port;
189 
190 	// Send
191 	Com_DevPrintf (0, "CL_SendConnectPacket: protocol=%d, port=%d, challenge=%u\n", cls.serverProtocol, port, cls.challenge);
192 	if (cls.serverProtocol == ENHANCED_PROTOCOL_VERSION)
193 		Netchan_OutOfBandPrint (NS_CLIENT, &adr, "connect %i %i %i \"%s\" %u\n",
194 			cls.serverProtocol, port, cls.challenge, Cvar_BitInfo (CVAR_USERINFO), msgLen);
195 	else
196 		Netchan_OutOfBandPrint (NS_CLIENT, &adr, "connect %i %i %i \"%s\"\n",
197 			cls.serverProtocol, port, cls.challenge, Cvar_BitInfo (CVAR_USERINFO));
198 }
199 
200 
201 /*
202 =====================
203 CL_ResetServerCount
204 =====================
205 */
CL_ResetServerCount(void)206 void CL_ResetServerCount (void)
207 {
208 	cl.serverCount = -1;
209 }
210 
211 
212 /*
213 =================
214 CL_SetRefConfig
215 =================
216 */
CL_SetRefConfig(void)217 void CL_SetRefConfig (void)
218 {
219 	if (!clMedia.initialized)
220 		return;
221 
222 	// Update client
223 	R_GetRefConfig (&cls.refConfig);
224 
225 	// Update modules
226 	CL_CGModule_SetRefConfig ();
227 
228 	// Re-check console wordwrap size after video size setup
229 	CL_ConsoleCheckResize ();
230 }
231 
232 
233 /*
234 =====================
235 CL_SetState
236 =====================
237 */
CL_SetState(caState_t state)238 void CL_SetState (caState_t state)
239 {
240 	// Store the state
241 	Com_SetClientState (state);
242 
243 	// Apply any state-specific actions
244 	switch (state) {
245 	case CA_CONNECTING:
246 		// FIXME: only call if last state was < CA_CONNECTING?
247 		CL_CGModule_ForceMenuOff ();
248 		Snd_StopAllSounds ();
249 		break;
250 
251 	case CA_CONNECTED:
252 		Cvar_FixCheatVars ();
253 		break;
254 	}
255 }
256 
257 
258 /*
259 =====================
260 CL_ClearState
261 =====================
262 */
CL_ClearState(void)263 void CL_ClearState (void)
264 {
265 	// Stop sound
266 	Snd_StopAllSounds ();
267 
268 	// Wipe the entire cl structure
269 	memset (&cl, 0, sizeof (cl));
270 	memset (&cl_baseLines, 0, sizeof (cl_baseLines));
271 	memset (&cl_parseEntities, 0, sizeof (cl_parseEntities));
272 
273 	// Set defaults
274 	cl.maxClients = MAX_CS_CLIENTS;
275 
276 	// Clear the netChan
277 	MSG_Clear (&cls.netChan.message);
278 
279 	// Clear the demo buffer
280 	MSG_Init (&cl.demoBuffer, cl.demoFrame, sizeof (cl.demoFrame));
281 	cl.demoBuffer.allowOverflow = qTrue;
282 }
283 
284 
285 /*
286 =====================
287 CL_Disconnect
288 
289 Goes from a connected state to full screen console state
290 Sends a disconnect message to the server
291 This is also called on Com_Error, so it shouldn't cause any errors
292 =====================
293 */
CL_Disconnect(qBool openMenu)294 void CL_Disconnect (qBool openMenu)
295 {
296 	byte	final[32];
297 
298 	if (Com_ClientState () == CA_UNINITIALIZED)
299 		return;
300 	if (Com_ClientState () == CA_DISCONNECTED)
301 		goto done;
302 
303 	if (cl_timedemo->intVal) {
304 		int	time;
305 
306 		Com_Printf (0, "Timedemo complete...\n");
307 		time = Sys_Milliseconds () - cl.timeDemoStart;
308 		if (time > 0)
309 			Com_Printf (0, "%i frames, %3.1f seconds: %3.1f fps\n", cl.timeDemoFrames,
310 				time/1000.0, cl.timeDemoFrames*1000.0f / time);
311 	}
312 
313 	cls.connectCount = 0;
314 	cls.connectTime = -99999;	// CL_CheckForResend () will fire immediately
315 
316 	CIN_StopCinematic ();
317 
318 	if (cls.demoRecording)
319 		CL_StopDemoRecording ();
320 
321 	// Send a disconnect message to the server
322 	final[0] = CLC_STRINGCMD;
323 	strcpy ((char *)final+1, "disconnect");
324 	Netchan_Transmit (&cls.netChan, 11, final);
325 	Netchan_Transmit (&cls.netChan, 11, final);
326 	Netchan_Transmit (&cls.netChan, 11, final);
327 
328 	// Stop download
329 	if (cls.download.file) {
330 		fclose (cls.download.file);
331 		cls.download.file = NULL;
332 	}
333 
334 done:
335 	CM_UnloadMap ();
336 	CL_MediaRestart ();
337 
338 	CL_ClearState ();
339 	CL_SetState (CA_DISCONNECTED);
340 
341 	if (openMenu)
342 		CL_CGModule_MainMenu ();
343 
344 #ifdef CL_HTTPDL
345 	CL_HTTPDL_CancelDownloads (qTrue);
346 	cls.download.httpReferer[0] = '\0';
347 #endif
348 
349 	cls.serverMessage[0] = '\0';
350 	cls.serverName[0] = '\0';
351 	cls.serverProtocol = 0;
352 
353 	Cvar_Set ("$mapname", "", qTrue);
354 	Cvar_Set ("$game", "", qTrue);
355 
356 	// Swap games if needed
357 	Cvar_GetLatchedVars (CVAR_LATCH_SERVER);
358 
359 	// Drop loading plaque unless this is the initial game start
360 	SCR_EndLoadingPlaque ();
361 }
362 
363 /*
364 =============================================================================
365 
366 	CONNECTION-LESS PACKETS
367 
368 =============================================================================
369 */
370 
371 /*
372 =================
373 CL_ClientConnect_CP
374 
375 Server connection
376 =================
377 */
CL_ClientConnect_CP(void)378 static void CL_ClientConnect_CP (void)
379 {
380 	char		*p;
381 	char		*buff;
382 	int			i;
383 
384 	switch (Com_ClientState()) {
385 	case CA_CONNECTED:
386 		Com_DevPrintf (PRNT_WARNING, "Dup connect received. Ignored.\n");
387 		return;
388 
389 	case CA_DISCONNECTED:
390 		Com_DevPrintf (0, "Received connect when disconnected. Ignored.\n");
391 		return;
392 
393 	case CA_ACTIVE:
394 		Com_DevPrintf (0, "Illegal connect when already connected! (q2msgs?). Ignored.\n");
395 		return;
396 	}
397 
398 	Com_DevPrintf (0, "client_connect: new\n");
399 
400 	Netchan_Setup (NS_CLIENT, &cls.netChan, &cls.netFrom, cls.serverProtocol, cls.quakePort, 0);
401 
402 	// Parse arguments (R1Q2 enhanced protocol only)
403 	if (cls.serverProtocol == ENHANCED_PROTOCOL_VERSION) {
404 		buff = NET_AdrToString (&cls.netChan.remoteAddress);
405 		for (i=1 ; i<Cmd_Argc() ; i++) {
406 			p = Cmd_Argv (i);
407 			if (!strncmp (p, "dlserver=", 9)) {
408 #ifdef CL_HTTPL
409 				p += 9;
410 				Q_snprintfz (cls.download.httpReferer, sizeof (cls.download.httpReferer), "quake2://%s", buff);
411 				CL_HTTPDL_SetServer (p);
412 				if (cls.download.httpServer[0])
413 					Com_DevPrintf (0, "HTTP downloading enabled, using '%s'\n", cls.download.httpServer);
414 #endif
415 			}
416 #ifdef CL_ANTICHEAT
417 			else if (!strncmp (p, "ac=", 3)) {
418 				p += 3;
419 				if (!p[0])
420 					continue;
421 				i = atoi (p);
422 				if (i) {
423 					MSG_WriteByte (&cls.netChan.message, CLC_NOP);
424 					Netchan_Transmit (&cls.netChan, 0, NULL);
425 
426 					// Don't sit and stutter sounds while loading
427 					Snd_StopAllSounds ();
428 
429 					// Let the GUI system know
430 					// FIXME: Use this, durr
431 					Com_Printf (0, "Loading and initializing the anticheat module, please wait.\n");
432 					GUI_NamedGlobalEvent ("ACLoading");
433 					SCR_UpdateScreen ();
434 
435 					// Load the API
436 					if (!CL_ACAPI_Init ())
437 						Com_Printf (PRNT_ERROR, "Anticheat failed to load, trying to connect without it.\n");
438 				}
439 			}
440 #endif
441 		}
442 	}
443 
444 	MSG_WriteChar (&cls.netChan.message, CLC_STRINGCMD);
445 	MSG_WriteString (&cls.netChan.message, "new");
446 
447 	Key_SetDest (KD_GAME);
448 	CL_SetState (CA_CONNECTED);
449 	cls.forcePacket = qTrue;
450 }
451 
452 
453 /*
454 =================
455 CL_Info_CP
456 
457 Server responding to a status broadcast
458 =================
459 */
CL_Info_CP(void)460 static void CL_Info_CP (void)
461 {
462 	CL_CGModule_ParseServerInfo (NET_AdrToString (&cls.netFrom), MSG_ReadString (&cls.netMessage));
463 }
464 
465 
466 /*
467 =================
468 CL_Cmd_CP
469 
470 Remote command from gui front end
471 =================
472 */
CL_Cmd_CP(void)473 static void CL_Cmd_CP (void)
474 {
475 	char	*s;
476 
477 	if (!NET_IsLocalAddress (cls.netFrom)) {
478 		Com_Printf (PRNT_WARNING, "Command packet from remote host. Ignored.\n");
479 		return;
480 	}
481 	Sys_AppActivate ();
482 	s = MSG_ReadString (&cls.netMessage);
483 	Cbuf_AddText (s);
484 	Cbuf_AddText ("\n");
485 }
486 
487 
488 /*
489 =================
490 CL_Ping_CP
491 
492 Ping from server
493 =================
494 */
CL_Ping_CP(void)495 static void CL_Ping_CP (void)
496 {
497 	// R1: stop users from exploiting your connecting, this can bog down even cable users!
498 	//Netchan_OutOfBandPrint (NS_CLIENT, &cls.netFrom, "ack");
499 }
500 
501 
502 /*
503 =================
504 CL_Challenge_CP
505 
506 Challenge from the server we are connecting to
507 =================
508 */
CL_Challenge_CP(void)509 static void CL_Challenge_CP (void)
510 {
511 	char	*p;
512 	int		protocol, i;
513 
514 	// Local challenge
515 	cls.challenge = atoi (Cmd_Argv (1));
516 	Com_DevPrintf (0, "client: received challenge\n");
517 
518 	// Flags
519 	protocol = ORIGINAL_PROTOCOL_VERSION;
520 	for (i=2 ; i<Cmd_Argc() ; i++) {
521 		p = Cmd_Argv (i);
522 		if (!strncmp (p, "p=", 2)) {
523 			p += 2;
524 			if (!p[0])
525 				continue;
526 			for ( ; ; ) {
527 				i = atoi (p);
528 				if (i == ENHANCED_PROTOCOL_VERSION)
529 					protocol = i;
530 				p = strchr (p, ',');
531 				if (!p)
532 					break;
533 				p++;
534 				if (!p[0])
535 					break;
536 			}
537 		}
538 	}
539 
540 	// Reset timer to avoid duplicates, and send the connect packet
541 	cls.connectTime = cls.realTime;
542 	CL_SendConnectPacket (protocol);
543 }
544 
545 
546 /*
547 =================
548 CL_Echo_CP
549 
550 Echo request from server
551 =================
552 */
CL_Echo_CP(void)553 static void CL_Echo_CP (void)
554 {
555 	Netchan_OutOfBandPrint (NS_CLIENT, &cls.netFrom, "%s", Cmd_Argv (1));
556 }
557 
558 
559 /*
560 =================
561 CL_ConnectionlessPacket
562 
563 Responses to broadcasts, etc
564 =================
565 */
CL_ConnectionlessPacket(void)566 static void CL_ConnectionlessPacket (void)
567 {
568 	char		*cmd, *printStr;
569 	netAdr_t	remote;
570 	qBool		isPrint;
571 
572 	MSG_BeginReading (&cls.netMessage);
573 	assert (MSG_ReadLong (&cls.netMessage) == -1);	// Skip the -1
574 
575 	cmd = MSG_ReadStringLine (&cls.netMessage);
576 	Cmd_TokenizeString (cmd, qFalse);
577 
578 	cmd = Cmd_Argv (0);
579 
580 	if (!strcmp (cmd, "client_connect")) {
581 		Com_Printf (0, "%s: %s\n", NET_AdrToString (&cls.netFrom), cmd);
582 		CL_ClientConnect_CP ();
583 		return;
584 	}
585 	else if (!strcmp (cmd, "info")) {
586 		CL_Info_CP ();
587 		return;
588 	}
589 	else if (!strcmp (cmd, "print")) {
590 		// Print command from somewhere
591 		isPrint = qTrue;
592 		printStr = MSG_ReadString (&cls.netMessage);
593 		if (CL_CGModule_ParseServerStatus (NET_AdrToString (&cls.netFrom), printStr))
594 			return;
595 		Q_strncpyz (cls.serverMessage, printStr, sizeof (cls.serverMessage));
596 	}
597 	else {
598 		isPrint = qFalse;
599 	}
600 
601 	// Only allow the following from current connected server and last destination client sent an rcon to
602 	NET_StringToAdr (cls.serverName, &remote);
603 	if (!NET_CompareBaseAdr (cls.netFrom, remote) && !NET_CompareBaseAdr (cls.netFrom, cl_lastRConTo)) {
604 		Com_Printf (PRNT_WARNING, "Illegal %s from %s. Ignored.\n", cmd, NET_AdrToString (&cls.netFrom));
605 		return;
606 	}
607 
608 	Com_Printf (0, "%s: %s\n", NET_AdrToString (&cls.netFrom), cmd);
609 
610 	if (isPrint)
611 		Com_Printf (0, "%s", printStr);
612 	else if (!strcmp (cmd, "cmd"))
613 		CL_Cmd_CP ();
614 	else if (!strcmp (cmd, "ping"))
615 		CL_Ping_CP ();
616 	else if (!strcmp (cmd, "challenge"))
617 		CL_Challenge_CP ();
618 	else if (!strcmp (cmd, "echo"))
619 		CL_Echo_CP ();
620 	else
621 		Com_Printf (PRNT_WARNING, "CL_ConnectionlessPacket: Unknown command.\n");
622 }
623 
624 /*
625 =============================================================================
626 
627 	MEDIA
628 
629 =============================================================================
630 */
631 
632 /*
633 =================
634 CL_ImageMediaInit
635 =================
636 */
CL_ImageMediaInit(void)637 void CL_ImageMediaInit (void)
638 {
639 	clMedia.cinMaterial			= R_RegisterPic ("***r_cinTexture***");
640 	clMedia.consoleShader		= R_RegisterPic ("pics/conback.tga");
641 	clMedia.whiteTexture		= R_RegisterPic ("***r_whiteTexture***");
642 	clMedia.blackTexture		= R_RegisterPic ("***r_blackTexture***");
643 }
644 
645 
646 /*
647 =================
648 CL_SoundMediaInit
649 =================
650 */
CL_SoundMediaInit(void)651 void CL_SoundMediaInit (void)
652 {
653 	int		i;
654 
655 	// Register local sounds
656 	clMedia.talkSfx = Snd_RegisterSound ("misc/talk.wav");
657 	for (i=1 ; i<MAX_CS_SOUNDS ; i++) {
658 		if (!cl.configStrings[CS_SOUNDS+i][0]) {
659 			cl.soundCfgStrings[i] = NULL;
660 			break;
661 		}
662 
663 		cl.soundCfgStrings[i] = Snd_RegisterSound (cl.configStrings[CS_SOUNDS+i]);
664 		Sys_SendKeyEvents ();	// pump message loop
665 	}
666 
667 	// Update CGame
668 	CL_CGModule_RegisterSounds ();
669 
670 	// Register GUI media
671 	GUI_RegisterSounds ();
672 }
673 
674 
675 /*
676 =================
677 CL_MediaInit
678 =================
679 */
CL_MediaInit(void)680 void CL_MediaInit (void)
681 {
682 	if (clMedia.initialized)
683 		return;
684 	if (Com_ClientState () == CA_UNINITIALIZED)
685 		return;
686 
687 	clMedia.initialized = qTrue;
688 
689 	// Free all sounds
690 	Snd_FreeSounds ();
691 
692 	// Init CGame api
693 	CL_CGameAPI_Init ();
694 
695 	// Register our media
696 	CL_ImageMediaInit ();
697 	CL_SoundMediaInit ();
698 
699 	// Check memory integrity
700 	Mem_CheckGlobalIntegrity ();
701 
702 	// Touch memory
703 	Mem_TouchGlobal ();
704 }
705 
706 
707 /*
708 =================
709 CL_MediaShutdown
710 =================
711 */
CL_MediaShutdown(void)712 void CL_MediaShutdown (void)
713 {
714 	if (!clMedia.initialized)
715 		return;
716 
717 	clMedia.initialized = qFalse;
718 
719 	// Shutdown CGame
720 	CL_CGameAPI_Shutdown ();
721 
722 	// Stop and free all sounds
723 	Snd_StopAllSounds ();
724 	Snd_FreeSounds ();
725 
726 	// Touch memory
727 	Mem_TouchGlobal ();
728 }
729 
730 
731 /*
732 =================
733 CL_MediaRestart
734 =================
735 */
CL_MediaRestart(void)736 void CL_MediaRestart (void)
737 {
738 	CL_MediaShutdown ();
739 	CL_MediaInit ();
740 }
741 
742 /*
743 =============================================================================
744 
745 	FRAME HANDLING
746 
747 =============================================================================
748 */
749 
750 /*
751 ==================
752 CL_ForcePacket
753 ==================
754 */
CL_ForcePacket(void)755 void CL_ForcePacket (void)
756 {
757 	cls.forcePacket = qTrue;
758 }
759 
760 
761 /*
762 =================
763 CL_CheckForResend
764 
765 Resend a connect message if the last one has timed out
766 =================
767 */
CL_CheckForResend(void)768 static void CL_CheckForResend (void)
769 {
770 	netAdr_t	adr;
771 
772 	// If the local server is running and we aren't then connect
773 	if (Com_ClientState() == CA_DISCONNECTED && Com_ServerState() >= SS_GAME) {
774 		CL_SetState (CA_CONNECTING);
775 		Q_strncpyz (cls.serverName, "localhost", sizeof (cls.serverName));
776 
777 		// We don't need a challenge on the localhost
778 		CL_SendConnectPacket (ORIGINAL_PROTOCOL_VERSION);
779 		return;
780 	}
781 
782 	// Resend if we haven't gotten a reply yet
783 	if (Com_ClientState () != CA_CONNECTING)
784 		return;
785 
786 	// Only resend every NET_RETRYDELAY
787 	if (cls.realTime - cls.connectTime < NET_RETRYDELAY)
788 		return;
789 
790 	// Check if it's a bad server address
791 	if (!NET_StringToAdr (cls.serverName, &adr)) {
792 		Com_Printf (PRNT_WARNING, "Bad server address\n");
793 		CL_SetState (CA_DISCONNECTED);
794 		CL_CGModule_MainMenu ();
795 		return;
796 	}
797 
798 	if (!adr.port)
799 		adr.port = BigShort (PORT_SERVER);
800 
801 	cls.connectCount++;
802 	cls.connectTime = cls.realTime;	// For retransmit requests
803 
804 	Com_Printf (0, "Connecting to %s...\n", cls.serverName);
805 
806 	Netchan_OutOfBandPrint (NS_CLIENT, &adr, "getchallenge\n");
807 }
808 
809 
810 /*
811 =================
812 CL_ReadPackets
813 =================
814 */
CL_ReadPackets(void)815 static void CL_ReadPackets (void)
816 {
817 	while (NET_GetPacket (NS_CLIENT, &cls.netFrom, &cls.netMessage)) {
818 		// Remote command packet
819 		if (*(int *)cls.netMessage.data == -1) {
820 			CL_ConnectionlessPacket ();
821 			continue;
822 		}
823 
824 		if (Com_ClientState () <= CA_CONNECTING)
825 			continue;		// Dump it if not connected
826 
827 		if (cls.netMessage.curSize < 8) {
828 			Com_DevPrintf (PRNT_WARNING, "%s: Runt packet\n", NET_AdrToString (&cls.netFrom));
829 			continue;
830 		}
831 
832 		// Packet from server
833 		if (!NET_CompareAdr (cls.netFrom, cls.netChan.remoteAddress)) {
834 			Com_DevPrintf (PRNT_WARNING, "%s: sequenced packet without connection\n", NET_AdrToString (&cls.netFrom));
835 			continue;
836 		}
837 
838 		if (!Netchan_Process (&cls.netChan, &cls.netMessage))
839 			continue;	// Shunned for some reason
840 
841 		// Force a packet update if a reliable was recieved
842 		if (cls.netChan.gotReliable && Com_ClientState() == CA_CONNECTED)
843 			cls.forcePacket = qTrue;
844 
845 		CL_ParseServerMessage ();
846 
847 		// We don't know if it is ok to save a demo message until after we have parsed the frame
848 		if (cls.demoRecording && !cls.demoWaiting && cls.serverProtocol == ORIGINAL_PROTOCOL_VERSION)
849 			CL_WriteDemoMessageFull ();
850 	}
851 
852 	// Check timeout
853 	if (Com_ClientState() >= CA_CONNECTED && cls.realTime-cls.netChan.lastReceived > cl_timeout->floatVal*1000) {
854 		if (++cl.timeOutCount > 5) {
855 			// Timeoutcount saves debugger
856 			Com_Printf (PRNT_WARNING, "Server connection timed out.\n");
857 			CL_Disconnect (qTrue);
858 			return;
859 		}
860 	}
861 	else
862 		cl.timeOutCount = 0;
863 }
864 
865 
866 /*
867 ==================
868 CL_SendCommand
869 ==================
870 */
CL_SendCommand(void)871 static void CL_SendCommand (void)
872 {
873 	// Send client packet to server
874 	CL_SendCmd ();
875 
876 	// Resend a connection request if necessary
877 	CL_CheckForResend ();
878 }
879 
880 
881 /*
882 ==================
883 CL_RefreshInputs
884 ==================
885 */
CL_RefreshInputs(void)886 static void CL_RefreshInputs (void)
887 {
888 	// Process new key events
889 	Sys_SendKeyEvents ();
890 
891 	// Process mice & joystick events
892 	IN_Commands ();
893 
894 	// Process console commands
895 	Cbuf_Execute ();
896 
897 	// Process packets from server
898 	CL_ReadPackets ();
899 
900 	// Update usercmd state
901 	CL_RefreshCmd ();
902 }
903 
904 
905 /*
906 ==================
907 CL_Frame
908 ==================
909 */
910 #define FRAMETIME_MAX 0.5f
CL_Frame(int msec)911 void __fastcall CL_Frame (int msec)
912 {
913 	static int	packetDelta = 0;
914 	static int	refreshDelta = 0;
915 	static int	miscDelta = 1000;
916 	qBool		packetFrame = qTrue;
917 	qBool		refreshFrame = qTrue;
918 	qBool		miscFrame = qTrue;
919 
920 	// Internal counters
921 	packetDelta += msec;
922 	refreshDelta += msec;
923 	miscDelta += msec;
924 
925 	// Frame counters
926 	cls.netFrameTime = packetDelta * 0.001f;
927 	cls.trueNetFrameTime = cls.netFrameTime;
928 	cls.refreshFrameTime = refreshDelta * 0.001f;
929 	cls.trueRefreshFrameTime = cls.refreshFrameTime;
930 	cls.realTime = Sys_Milliseconds ();
931 
932 	// If in the debugger last frame, don't timeout
933 	if (msec > 5000)
934 		cls.netChan.lastReceived = Sys_Milliseconds ();
935 
936 	// Don't extrapolate too far ahead
937 	if (cls.netFrameTime > FRAMETIME_MAX)
938 		cls.netFrameTime = FRAMETIME_MAX;
939 	if (cls.refreshFrameTime > FRAMETIME_MAX)
940 		cls.refreshFrameTime = FRAMETIME_MAX;
941 
942 	if (!cl_timedemo->intVal) {
943 		// Packet transmission rate is too high
944 		if (packetDelta < 1000.0f/cl_maxfps->floatVal)
945 			packetFrame = qFalse;
946 		// This happens when your video framerate plumits to near-below net 'framerate'
947 		else if (cls.netFrameTime == cls.refreshFrameTime)
948 			packetFrame = qFalse;
949 
950 		// Video framerate is too high
951 		if (refreshDelta < 1000.0f/r_maxfps->floatVal)
952 			refreshFrame = qFalse;
953 
954 		// Don't need to do this stuff much (10FPS)
955 		if (miscDelta < 1000/10)
956 			miscFrame = qFalse;
957 	}
958 
959 	// Don't flood packets out while connecting (10FPS)
960 	if (Com_ClientState () == CA_CONNECTED) {
961 		if (packetDelta < 1000/10)
962 			packetFrame = qFalse;
963 
964 #ifdef CL_HTTPDL
965 		CL_HTTPDL_RunDownloads ();
966 #endif
967 	}
968 
969 	// Update the inputs (keyboard, mouse, server, etc)
970 	CL_RefreshInputs ();
971 
972 	// Send commands to the server
973 	if (cls.forcePacket || com_userInfoModified) {
974 		if (Com_ClientState () < CA_CONNECTED)
975 			com_userInfoModified = qFalse;
976 		else
977 			packetFrame = qTrue;
978 		cls.forcePacket = qFalse;
979 	}
980 
981 	if (packetFrame) {
982 		packetDelta = 0;
983 		CL_SendCommand ();
984 
985 #ifdef CL_HTTPDL
986 		CL_HTTPDL_RunDownloads ();
987 #endif
988 	}
989 
990 	// Render the display
991 	if (refreshFrame) {
992 		refreshDelta = 0;
993 
994 		// Stuff that does not need to happen a lot
995 		if (miscFrame) {
996 			miscDelta = 0;
997 
998 			// Let the mouse activate or deactivate
999 			IN_Frame ();
1000 
1001 			// Queued video restart
1002 			VID_CheckChanges (&cls.refConfig);
1003 
1004 			// Queued audio restart
1005 			Snd_CheckChanges ();
1006 		}
1007 
1008 		// Update the screen
1009 		SCR_UpdateScreen ();
1010 
1011 		// Advance local effects for next frame
1012 		CIN_RunCinematic ();
1013 
1014 		// Update audio orientation
1015 		if (Com_ClientState () != CA_ACTIVE || cl.cin.time > 0)
1016 			Snd_Update (NULL);
1017 
1018 		if (miscFrame)
1019 			CDAudio_Update ();
1020 	}
1021 }
1022 
1023 /*
1024 =============================================================================
1025 
1026 	CONSOLE FUNCTIONS
1027 
1028 =============================================================================
1029 */
1030 
1031 /*
1032 =================
1033 CL_Changing_f
1034 
1035 Just sent as a hint to the client that they should
1036 drop to full console
1037 =================
1038 */
CL_Changing_f(void)1039 static void CL_Changing_f (void)
1040 {
1041 	// If we are downloading, we don't change! This so we don't suddenly stop downloading a map
1042 	if (cls.download.file)
1043 		return;
1044 
1045 	if (cls.demoRecording)
1046 		CL_StopDemoRecording ();
1047 
1048 	// Update the screen
1049 	SCR_UpdateScreen ();
1050 
1051 	// Prevent manual issuing crashing game
1052 	if (Com_ClientState() < CA_CONNECTED)
1053 		return;
1054 
1055 	cls.connectCount = 0;
1056 
1057 	SCR_BeginLoadingPlaque ();
1058 	CL_SetState (CA_CONNECTED);	// Not active anymore, but not disconnected
1059 	Com_Printf (0, "\nChanging map...\n");
1060 }
1061 
1062 
1063 /*
1064 ================
1065 CL_Connect_f
1066 ================
1067 */
CL_Connect_f(void)1068 static void CL_Connect_f (void)
1069 {
1070 	char		*server;
1071 	netAdr_t	adr;
1072 
1073 	if (Cmd_Argc() != 2) {
1074 		Com_Printf (0, "usage: connect <server>\n");
1075 		return;
1076 	}
1077 
1078 	if (Com_ServerState()) {
1079 		// If running a local server, kill it and reissue
1080 		SV_ServerShutdown ("Server quit\n", qFalse, qFalse);
1081 	}
1082 	server = Cmd_Argv (1);
1083 
1084 	NET_Config (NET_CLIENT);
1085 
1086 	// Validate the address
1087 	if (!NET_StringToAdr (server, &adr)) {
1088 		Com_Printf (PRNT_WARNING, "Bad server address: %s\n", server);
1089 		return;
1090 	}
1091 
1092 	if (!adr.port)
1093 		adr.port = BigShort (PORT_SERVER);
1094 
1095 	// Disconnect if connected
1096 	CL_Disconnect (qFalse);
1097 
1098 	// Reset the protocol attempt if we're connecting to a different server
1099 	if (!NET_CompareAdr (adr, cls.netChan.remoteAddress)) {
1100 		Com_DevPrintf (0, "Resetting protocol attempt since %s", NET_AdrToString (&adr));
1101 		Com_DevPrintf (0, " is not %s.\n", NET_AdrToString (&cls.netChan.remoteAddress));
1102 		cls.serverProtocol = 0;
1103 	}
1104 
1105 	// Set the client state and prepare to connect
1106 	CL_SetState (CA_CONNECTING);
1107 	cls.serverMessage[0] = '\0';
1108 	Q_strncpyz (cls.serverName, server, sizeof (cls.serverName));
1109 	Q_strncpyz (cls.serverNameLast, server, sizeof (cls.serverNameLast));
1110 
1111 	cls.connectTime = -99999;	// CL_CheckForResend () will fire immediately
1112 	cls.connectCount = 0;
1113 }
1114 
1115 
1116 /*
1117 =================
1118 CL_Disconnect_f
1119 =================
1120 */
CL_Disconnect_f(void)1121 static void CL_Disconnect_f (void)
1122 {
1123 	if (Com_ClientState () == CA_DISCONNECTED)
1124 		return;
1125 
1126 	cls.serverProtocol = 0;
1127 	Com_Error (ERR_DROP, "Disconnected from server");
1128 }
1129 
1130 
1131 /*
1132 ===============
1133 CL_Download_f
1134 
1135 Request a download from the server
1136 ===============
1137 */
CL_Download_f(void)1138 static void CL_Download_f (void)
1139 {
1140 	char	*fileName;
1141 
1142 	if (Cmd_Argc () != 2) {
1143 		Com_Printf (0, "Usage: download <filename>\n");
1144 		return;
1145 	}
1146 
1147 	if (Com_ClientState() < CA_CONNECTED) {
1148 		Com_Printf (PRNT_WARNING, "Not connected.\n");
1149 		return;
1150 	}
1151 
1152 	// It exists, no need to download
1153 	fileName = Cmd_Argv (1);
1154 	if (FS_FileExists (fileName) != -1) {
1155 		Com_Printf (PRNT_WARNING, "File already exists.\n");
1156 		return;
1157 	}
1158 
1159 	CL_CheckOrDownloadFile (fileName);
1160 }
1161 
1162 
1163 /*
1164 ==================
1165 CL_ForwardToServer_f
1166 ==================
1167 */
CL_ForwardToServer_f(void)1168 static void CL_ForwardToServer_f (void)
1169 {
1170 	if (Com_ClientState() < CA_CONNECTED) {
1171 		Com_Printf (0, "Can't \"%s\", not connected\n", Cmd_Argv (0));
1172 		return;
1173 	}
1174 
1175 	// Don't forward the first argument
1176 	if (Cmd_Argc () > 1) {
1177 		MSG_WriteByte (&cls.netChan.message, CLC_STRINGCMD);
1178 		MSG_WriteStringCat (&cls.netChan.message, Cmd_Args ());
1179 		cls.forcePacket = qTrue;
1180 	}
1181 }
1182 
1183 
1184 /*
1185 ==================
1186 CL_Pause_f
1187 ==================
1188 */
CL_Pause_f(void)1189 static void CL_Pause_f (void)
1190 {
1191 	// Never pause in multiplayer
1192 	if (Cvar_GetFloatValue ("maxclients") > 1 || !Com_ServerState ()) {
1193 		Cvar_SetValue ("paused", 0, qFalse);
1194 		return;
1195 	}
1196 
1197 	Cvar_SetValue ("paused", !cl_paused->intVal, qFalse);
1198 }
1199 
1200 
1201 /*
1202 =================
1203 CL_PingLocal_f
1204 =================
1205 */
CL_PingLocal_f(void)1206 static void CL_PingLocal_f (void)
1207 {
1208 	netAdr_t	adr;
1209 
1210 	NET_Config (NET_CLIENT);
1211 
1212 	// Send a broadcast packet
1213 	Com_Printf (0, "info pinging broadcast...\n");
1214 
1215 	adr.naType = NA_BROADCAST;
1216 	adr.port = BigShort (PORT_SERVER);
1217 	Netchan_OutOfBandPrint (NS_CLIENT, &adr, Q_VarArgs ("info %i", ORIGINAL_PROTOCOL_VERSION));
1218 }
1219 
1220 
1221 /*
1222 =================
1223 CL_PingServer_f
1224 =================
1225 */
CL_PingServer_f(void)1226 static void CL_PingServer_f (void)
1227 {
1228 	netAdr_t	adr;
1229 	char		*adrString;
1230 
1231 	if (Cmd_Argc() != 2) {
1232 		Com_Printf (0, "usage: pingserver <address>[:port]\n");
1233 		return;
1234 	}
1235 
1236 	NET_Config (NET_CLIENT);
1237 
1238 	adrString = Cmd_Argv (1);
1239 	if (!adrString || !adrString[0])
1240 		return;
1241 
1242 	Com_Printf (0, "pinging %s...\n", adrString);
1243 
1244 	if (!NET_StringToAdr (adrString, &adr)) {
1245 		Com_Printf (PRNT_WARNING, "Bad address: %s\n", adrString);
1246 		return;
1247 	}
1248 
1249 	if (!adr.port)
1250 		adr.port = BigShort (PORT_SERVER);
1251 
1252 	Netchan_OutOfBandPrint (NS_CLIENT, &adr, Q_VarArgs ("info %i", ORIGINAL_PROTOCOL_VERSION));
1253 }
1254 
1255 
1256 /*
1257 =================
1258 CL_Precache_f
1259 
1260 The server will send this command right
1261 before allowing the client into the server
1262 =================
1263 */
CL_Precache_f(void)1264 static void CL_Precache_f (void)
1265 {
1266 	// Yet another hack to let old demos work the old precache sequence
1267 	if (Cmd_Argc () < 2) {
1268 		uint32	mapCheckSum;	// For detecting cheater maps
1269 
1270 		CM_LoadMap (cl.configStrings[CS_MODELS+1], qTrue, &mapCheckSum);
1271 		CL_CGModule_LoadMap ();
1272 		return;
1273 	}
1274 
1275 	CL_ResetDownload ();
1276 	CL_RequestNextDownload ();
1277 }
1278 
1279 
1280 /*
1281 ==================
1282 CL_Quit_f
1283 ==================
1284 */
CL_Quit_f(void)1285 static void CL_Quit_f (void)
1286 {
1287 	CL_Disconnect (qFalse);
1288 	Com_Quit (qFalse);
1289 }
1290 
1291 
1292 /*
1293 =====================
1294 CL_Rcon_f
1295 
1296 Send the rest of the command line over as an unconnected command.
1297 =====================
1298 */
CL_Rcon_f(void)1299 static void CL_Rcon_f (void)
1300 {
1301 	char		message[1024];
1302 	int			i;
1303 	netAdr_t	to;
1304 
1305 	if (!rcon_clpassword->string) {
1306 		Com_Printf (0, "You must set 'rcon_password' before\n" "issuing an rcon command.\n");
1307 		return;
1308 	}
1309 
1310 	// R1: check buffer size
1311 	if ((strlen(Cmd_Args())+strlen(rcon_clpassword->string)+16) >= sizeof(message)) {
1312 		Com_Printf (0, "Length of password + command exceeds maximum allowed length.\n");
1313 		return;
1314 	}
1315 
1316 	message[0] = (char)255;
1317 	message[1] = (char)255;
1318 	message[2] = (char)255;
1319 	message[3] = (char)255;
1320 	message[4] = 0;
1321 
1322 	NET_Config (NET_CLIENT);
1323 
1324 	Q_strcatz (message, "rcon ", sizeof (message));
1325 
1326 	Q_strcatz (message, rcon_clpassword->string, sizeof (message));
1327 	Q_strcatz (message, " ", sizeof (message));
1328 
1329 	for (i=1 ; i<Cmd_Argc () ; i++) {
1330 		Q_strcatz (message, Cmd_Argv (i), sizeof (message));
1331 		Q_strcatz (message, " ", sizeof (message));
1332 	}
1333 
1334 	if (Com_ClientState () >= CA_CONNECTED) {
1335 		to = cls.netChan.remoteAddress;
1336 		cl_lastRConTo = cls.netChan.remoteAddress;
1337 	}
1338 	else {
1339 		if (!strlen (rcon_address->string)) {
1340 			Com_Printf (0, "You must either be connected,\n"
1341 									"or set the 'rcon_address' cvar\n"
1342 									"to issue rcon commands\n");
1343 
1344 			return;
1345 		}
1346 
1347 		NET_StringToAdr (rcon_address->string, &to);
1348 		NET_StringToAdr (rcon_address->string, &cl_lastRConTo);
1349 
1350 		if (to.port == 0) {
1351 			to.port = BigShort (PORT_SERVER);
1352 			cl_lastRConTo.port = to.port;
1353 		}
1354 	}
1355 
1356 	NET_SendPacket (NS_CLIENT, strlen(message)+1, message, &to);
1357 }
1358 
1359 
1360 /*
1361 =================
1362 CL_Reconnect_f
1363 
1364 The server is changing levels, or the user input the command.
1365 =================
1366 */
CL_Reconnect_f(void)1367 void CL_Reconnect_f (void)
1368 {
1369 	// If we are downloading, we don't change! This so we don't suddenly stop downloading a map
1370 	if (cls.download.file)
1371 		return;
1372 
1373 	cls.connectCount = 0;
1374 
1375 	Snd_StopAllSounds ();
1376 
1377 	if (Com_ClientState () == CA_CONNECTED) {
1378 		Com_Printf (0, "reconnecting (soft)...\n");
1379 		MSG_WriteChar (&cls.netChan.message, CLC_STRINGCMD);
1380 		MSG_WriteString (&cls.netChan.message, "new");
1381 		cls.forcePacket = qTrue;
1382 		return;
1383 	}
1384 
1385 	if (cls.serverName[0]) {
1386 		if (Com_ClientState () >= CA_CONNECTED) {
1387 			Com_Printf (0, "Disconnecting...\n");
1388 			Q_strncpyz (cls.serverName, cls.serverNameLast, sizeof (cls.serverName));
1389 			CL_Disconnect (qFalse);
1390 			cls.connectTime = cls.realTime - (NET_RETRYDELAY*0.5f);
1391 		}
1392 		else {
1393 			cls.connectTime = -99999; // CL_CheckForResend () will fire immediately
1394 		}
1395 
1396 		CL_SetState (CA_CONNECTING);
1397 	}
1398 
1399 	if (cls.serverNameLast[0]) {
1400 		cls.connectTime = -99999; // CL_CheckForResend () will fire immediately
1401 		CL_SetState (CA_CONNECTING);
1402 		Com_Printf (0, "reconnecting (hard)...\n");
1403 		Q_strncpyz (cls.serverName, cls.serverNameLast, sizeof (cls.serverName));
1404 	}
1405 	else {
1406 		Com_Printf (0, "No server to reconnect to...\n");
1407 	}
1408 }
1409 
1410 
1411 /*
1412 ====================
1413 CL_Record_f
1414 
1415 record <demoname>
1416 
1417 Begins recording a demo from the current position
1418 ====================
1419 */
CL_Record_f(void)1420 static void CL_Record_f (void)
1421 {
1422 	char			name[MAX_OSPATH];
1423 
1424 	if (Cmd_Argc () != 2) {
1425 		Com_Printf (0, "record <demoname>\n");
1426 		return;
1427 	}
1428 
1429 	if (cls.demoRecording) {
1430 		Com_Printf (0, "Already recording.\n");
1431 		return;
1432 	}
1433 
1434 	if (Com_ClientState () != CA_ACTIVE) {
1435 		Com_Printf (0, "You must be in a level to record.\n");
1436 		return;
1437 	}
1438 
1439 	if (cl.attractLoop) {
1440 		Com_Printf (0, "Unable to record from a demo stream due to insufficient deltas.\n");
1441 		return;
1442 	}
1443 
1444 	if (strstr (Cmd_Argv(1), "..") || strchr (Cmd_Argv(1), '/') || strchr (Cmd_Argv(1), '\\')) {
1445 		Com_Printf (0, "Illegal filename.\n");
1446 		return;
1447 	}
1448 
1449 	// Start recording
1450 	Q_snprintfz (name, sizeof (name), "demos/%s.dm2", Cmd_Argv (1));
1451 	if (CL_StartDemoRecording (name)) {
1452 		Com_Printf (0, "recording to %s.\n", name);
1453 	}
1454 	else {
1455 		Com_Printf (PRNT_ERROR, "CL_Record_f: couldn't open.\n");
1456 	}
1457 }
1458 
1459 
1460 /*
1461 ===================
1462 CL_Setenv_f
1463 ===================
1464 */
CL_Setenv_f(void)1465 static void CL_Setenv_f (void)
1466 {
1467 	int argc = Cmd_Argc ();
1468 
1469 	if (argc > 2) {
1470 		char buffer[1000];
1471 		int i;
1472 
1473 		Q_strncpyz (buffer, Cmd_Argv (1), sizeof (buffer)-1);
1474 		Q_strcatz (buffer, "=", sizeof (buffer));
1475 
1476 		for (i=2 ; i<argc ; i++) {
1477 			Q_strcatz (buffer, Cmd_Argv (i), sizeof (buffer));
1478 			Q_strcatz (buffer, " ", sizeof (buffer));
1479 		}
1480 
1481 		putenv (buffer);
1482 	}
1483 	else if (argc == 2) {
1484 		char *env = getenv (Cmd_Argv (1));
1485 
1486 		if (env)
1487 			Com_Printf (0, "\"%s\" is: \"%s\"\n", Cmd_Argv (1), env);
1488 		else
1489 			Com_Printf (0, "\"%s\" is undefined\n", Cmd_Argv (1), env);
1490 	}
1491 }
1492 
1493 
1494 /*
1495 ===================
1496 CL_ServerStatus_f
1497 ===================
1498 */
CL_ServerStatus_f(void)1499 static void CL_ServerStatus_f (void)
1500 {
1501 	char		message[16];
1502 	netAdr_t	adr;
1503 
1504 	if (Cmd_Argc() != 2) {
1505 		Com_Printf (0, "usage: sstatus <address>[:port]\n");
1506 		return;
1507 	}
1508 
1509 	memset (&message, 0, sizeof (message));
1510 	message[0] = (char)255;
1511 	message[1] = (char)255;
1512 	message[2] = (char)255;
1513 	message[3] = (char)255;
1514 	message[4] = (char)0;
1515 	Q_strcatz (message, "status", sizeof (message));
1516 
1517 	NET_Config (NET_CLIENT);
1518 
1519 	NET_StringToAdr (Cmd_Argv (1), &adr);
1520 
1521 	if (!adr.port)
1522 		adr.port = BigShort (PORT_SERVER);
1523 
1524 	NET_SendPacket (NS_CLIENT, strlen(message)+1, message, &adr);
1525 }
1526 
1527 
1528 /*
1529 ===================
1530 CL_StatusLocal_f
1531 ===================
1532 */
CL_StatusLocal_f(void)1533 static void CL_StatusLocal_f (void)
1534 {
1535 	char		message[16];
1536 	int		messageLen;
1537 	netAdr_t	adr;
1538 
1539 	memset (&message, 0, sizeof (message));
1540 	message[0] = (char)255;
1541 	message[1] = (char)255;
1542 	message[2] = (char)255;
1543 	message[3] = (char)255;
1544 	message[4] = (char)0;
1545 	Q_strcatz (message, "status", sizeof (message));
1546 	messageLen = (int)strlen (message) + 1;
1547 
1548 	NET_Config (NET_CLIENT);
1549 
1550 	// Send a broadcast packet
1551 	Com_Printf (0, "status pinging broadcast...\n");
1552 
1553 	adr.naType = NA_BROADCAST;
1554 	adr.port = BigShort (PORT_SERVER);
1555 	Netchan_OutOfBand (NS_CLIENT, &adr, messageLen, (byte *)message);
1556 }
1557 
1558 
1559 /*
1560 ====================
1561 CL_Stop_f
1562 
1563 Stop recording a demo
1564 ====================
1565 */
CL_Stop_f(void)1566 static void CL_Stop_f (void)
1567 {
1568 	int		len;
1569 
1570 	if (!cls.demoRecording) {
1571 		Com_Printf (0, "Not recording a demo.\n");
1572 		return;
1573 	}
1574 
1575 	len = FS_FileLength (cls.demoFile);
1576 
1577 	CL_StopDemoRecording ();
1578 
1579 	Com_Printf (0, "Stopped recording, wrote %d bytes\n", len);
1580 }
1581 
1582 
1583 /*
1584 ==============
1585 CL_Userinfo_f
1586 ==============
1587 */
CL_Userinfo_f(void)1588 static void CL_Userinfo_f (void)
1589 {
1590 	Com_Printf (0, "User info settings:\n");
1591 	Info_Print (Cvar_BitInfo (CVAR_USERINFO));
1592 }
1593 
1594 /*
1595 =============================================================================
1596 
1597 	INIT / SHUTDOWN
1598 
1599 =============================================================================
1600 */
1601 
1602 /*
1603 ==================
1604 CL_WriteConfig
1605 ==================
1606 */
CL_WriteConfig(void)1607 static void CL_WriteConfig (void)
1608 {
1609 	FILE	*f;
1610 	char	path[MAX_QPATH];
1611 
1612 	Cvar_GetLatchedVars (CVAR_LATCH_AUDIO|CVAR_LATCH_SERVER|CVAR_LATCH_VIDEO|CVAR_RESET_GAMEDIR);
1613 
1614 	Com_Printf (0, "Saving configuration...\n");
1615 
1616 	if (Cmd_Argc () == 2)
1617 		Q_snprintfz (path, sizeof (path), "%s/%s", FS_Gamedir (), Cmd_Argv (1));
1618 	else
1619 		Q_snprintfz (path, sizeof (path), "%s/eglcfg.cfg", FS_Gamedir ());
1620 
1621 	f = fopen (path, "wt");
1622 	if (!f) {
1623 		Com_Printf (PRNT_ERROR, "ERROR: Couldn't write %s\n", path);
1624 		return;
1625 	}
1626 
1627 	fprintf (f, "//\n// configuration file generated by EGL, modify at risk\n//\n");
1628 
1629 	fprintf (f, "\n// key bindings\n");
1630 	Key_WriteBindings (f);
1631 
1632 	fprintf (f, "\n// console variables\n");
1633 	Cvar_WriteVariables (f);
1634 
1635 	fprintf (f, "\n\0");
1636 	fclose (f);
1637 	Com_Printf (0, "Saved to %s\n", path);
1638 }
1639 
1640 
1641 /*
1642 =================
1643 CL_Register
1644 =================
1645 */
CL_Register(void)1646 static void CL_Register (void)
1647 {
1648 	// Register our variables
1649 	allow_download			= Cvar_Register ("allow_download",			"1",	CVAR_ARCHIVE);
1650 	allow_download_players	= Cvar_Register ("allow_download_players",	"0",	CVAR_ARCHIVE);
1651 	allow_download_models	= Cvar_Register ("allow_download_models",	"1",	CVAR_ARCHIVE);
1652 	allow_download_sounds	= Cvar_Register ("allow_download_sounds",	"1",	CVAR_ARCHIVE);
1653 	allow_download_maps		= Cvar_Register ("allow_download_maps",		"1",	CVAR_ARCHIVE);
1654 
1655 	cl_downloadToBase		= Cvar_Register ("cl_downloadToBase",		"0",		CVAR_ARCHIVE);
1656 	cl_lightlevel			= Cvar_Register ("r_lightlevel",			"0",		0);
1657 	cl_maxfps				= Cvar_Register ("cl_maxfps",				"30",		0);
1658 	r_maxfps				= Cvar_Register ("r_maxfps",				"500",		0);
1659 	cl_paused				= Cvar_Register ("paused",					"0",		CVAR_CHEAT);
1660 	cl_protocol				= Cvar_Register ("cl_protocol",				"0",		0);
1661 
1662 	cl_shownet				= Cvar_Register ("cl_shownet",				"0",		0);
1663 
1664 	cl_stereo_separation	= Cvar_Register ("cl_stereo_separation",	"0.4",		CVAR_ARCHIVE);
1665 
1666 	cl_timedemo				= Cvar_Register ("timedemo",				"0",		CVAR_CHEAT);
1667 	cl_timeout				= Cvar_Register ("cl_timeout",				"120",		0);
1668 	cl_timestamp			= Cvar_Register ("cl_timestamp",			"0",		CVAR_ARCHIVE);
1669 
1670 	con_chatHud				= Cvar_Register ("con_chatHud",				"0",		CVAR_ARCHIVE);
1671 	con_chatHudLines		= Cvar_Register ("con_chatHudLines",		"4",		CVAR_ARCHIVE);
1672 	con_chatHudPosX			= Cvar_Register ("con_chatHudPosX",			"8",		CVAR_ARCHIVE);
1673 	con_chatHudPosY			= Cvar_Register ("con_chatHudPosY",			"-14",		CVAR_ARCHIVE);
1674 	con_chatHudShadow		= Cvar_Register ("con_chatHudShadow",		"0",		CVAR_ARCHIVE);
1675 	con_notifyfade			= Cvar_Register ("con_notifyfade",			"1",		CVAR_ARCHIVE);
1676 	con_notifylarge			= Cvar_Register ("con_notifylarge",			"0",		CVAR_ARCHIVE);
1677 	con_notifylines			= Cvar_Register ("con_notifylines",			"4",		CVAR_ARCHIVE);
1678 	con_notifytime			= Cvar_Register ("con_notifytime",			"3",		CVAR_ARCHIVE);
1679 	con_alpha				= Cvar_Register ("con_alpha",				"1",		CVAR_ARCHIVE);
1680 	con_clock				= Cvar_Register ("con_clock",				"1",		CVAR_ARCHIVE);
1681 	con_drop				= Cvar_Register ("con_drop",				"0.5",		CVAR_ARCHIVE);
1682 	con_scroll				= Cvar_Register ("con_scroll",				"2",		CVAR_ARCHIVE);
1683 
1684 	freelook				= Cvar_Register ("freelook",				"0",		CVAR_ARCHIVE);
1685 
1686 	lookspring				= Cvar_Register ("lookspring",				"0",		CVAR_ARCHIVE);
1687 	lookstrafe				= Cvar_Register ("lookstrafe",				"0",		CVAR_ARCHIVE);
1688 
1689 	m_forward				= Cvar_Register ("m_forward",				"1",		0);
1690 	m_pitch					= Cvar_Register ("m_pitch",					"0.022",	CVAR_ARCHIVE);
1691 	m_side					= Cvar_Register ("m_side",					"1",		0);
1692 	m_yaw					= Cvar_Register ("m_yaw",					"0.022",	0);
1693 
1694 	rcon_clpassword			= Cvar_Register ("rcon_password",			"",			0);
1695 	rcon_address			= Cvar_Register ("rcon_address",			"",			0);
1696 
1697 	r_fontScale				= Cvar_Register ("r_fontScale",				"1",		CVAR_ARCHIVE);
1698 
1699 	sensitivity				= Cvar_Register ("sensitivity",				"3",		CVAR_ARCHIVE);
1700 
1701 	scr_conspeed			= Cvar_Register ("scr_conspeed",			"3",		0);
1702 
1703 	Cmd_AddCommand ("changing",			CL_Changing_f,			"");
1704 	Cmd_AddCommand ("connect",			CL_Connect_f,			"Connects to a server");
1705 	Cmd_AddCommand ("cmd",				CL_ForwardToServer_f,	"Forwards a command to the server");
1706 	Cmd_AddCommand ("disconnect",		CL_Disconnect_f,		"Disconnects from the current server");
1707 	Cmd_AddCommand ("download",			CL_Download_f,			"Manually download a file from the server");
1708 	Cmd_AddCommand ("exit",				CL_Quit_f,				"Exits");
1709 	Cmd_AddCommand ("pause",			CL_Pause_f,				"Pauses the game");
1710 	Cmd_AddCommand ("pingserver",		CL_PingServer_f,		"Sends an info request packet to a server");
1711 	Cmd_AddCommand ("pinglocal",		CL_PingLocal_f,			"Queries broadcast for an info string");
1712 	Cmd_AddCommand ("precache",			CL_Precache_f,			"");
1713 	Cmd_AddCommand ("quit",				CL_Quit_f,				"Exits");
1714 	Cmd_AddCommand ("rcon",				CL_Rcon_f,				"");
1715 	Cmd_AddCommand ("reconnect",		CL_Reconnect_f,			"Reconnect to the current server");
1716 	Cmd_AddCommand ("record",			CL_Record_f,			"Start recording a demo");
1717 	Cmd_AddCommand ("savecfg",			CL_WriteConfig,			"Saves current settings to the configuration file");
1718 	Cmd_AddCommand ("setenv",			CL_Setenv_f,			"");
1719 	Cmd_AddCommand ("statuslocal",		CL_StatusLocal_f,		"Queries broadcast for a status string");
1720 	Cmd_AddCommand ("sstatus",			CL_ServerStatus_f,		"Sends a status request packet to a server");
1721 	Cmd_AddCommand ("stop",				CL_Stop_f,				"Stop recording a demo");
1722 	Cmd_AddCommand ("userinfo",			CL_Userinfo_f,			"");
1723 
1724 #if defined(CL_ANTICHEAT) && defined(_DEBUG)
1725 	Cmd_AddCommand ("initanticheat",	CL_ACAPI_Init,			"");
1726 #endif // CL_ANTICHEAT && _DEBUG
1727 }
1728 
1729 
1730 /*
1731 ====================
1732 CL_ClientInit
1733 ====================
1734 */
CL_ClientInit(void)1735 void CL_ClientInit (void)
1736 {
1737 	int		i;
1738 
1739 	// Fill with strings so we're not if'ing every time it's used
1740 	for (i=SVC_MAX ; i<256 ; i++)
1741 		cl_svcStrings[i] = cl_svcStrings[0];
1742 
1743 	// All archived variables will now be loaded
1744 	CL_Register ();
1745 
1746 	// Create memory pools
1747 	cl_cGameSysPool = Mem_CreatePool ("Client: CGame system");
1748 	cl_cinSysPool = Mem_CreatePool ("Client: Cinematic system");
1749 	cl_guiSysPool = Mem_CreatePool ("Client: GUI system");
1750 	cl_soundSysPool = Mem_CreatePool ("Client: Sound system");
1751 
1752 	// Initialize video/sound
1753 	VID_Init (&cls.refConfig);
1754 
1755 	// Initialize the net buffer
1756 	MSG_Init (&cls.netMessage, cls.netBuffer, sizeof (cls.netBuffer));
1757 
1758 	CL_SetState (CA_DISCONNECTED);
1759 	cls.realTime = Sys_Milliseconds ();
1760 	cls.disableScreen = qTrue;
1761 
1762 	// Initialize subsystems
1763 	CDAudio_Init ();
1764 	CL_InputInit ();
1765 	IN_Init ();
1766 
1767 #ifdef CL_HTTPDL
1768 	CL_HTTPDL_Init ();
1769 #endif
1770 
1771 	// Load client media
1772 	CL_MediaInit ();
1773 	GUI_Init ();
1774 	CL_CGModule_MainMenu ();
1775 
1776 	// Touch memory
1777 	Mem_TouchGlobal ();
1778 
1779 	// Ready!
1780 	cls.disableScreen = qFalse;
1781 }
1782 
1783 
1784 /*
1785 ===============
1786 CL_ClientShutdown
1787 
1788 FIXME: this is a callback from Sys_Quit Sys_Error and Com_Error. It would be better
1789 to run quit through here before the final handoff to the sys code.
1790 ===============
1791 */
CL_ClientShutdown(qBool error)1792 void CL_ClientShutdown (qBool error)
1793 {
1794 	static qBool isDown = qFalse;
1795 	if (isDown)
1796 		return;
1797 	isDown = qTrue;
1798 
1799 #ifdef CL_HTTPDL
1800 	CL_HTTPDL_Cleanup (qTrue);
1801 #endif
1802 
1803 	if (!error)
1804 		CL_WriteConfig ();
1805 
1806 	CL_CGameAPI_Shutdown ();
1807 
1808 	CDAudio_Shutdown ();
1809 	Snd_Shutdown ();
1810 
1811 	IN_Shutdown ();
1812 	VID_Shutdown ();
1813 }
1814