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