1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 Copyright (C) 2011 COR Entertainment, LLC.
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20 */
21 // cl_main.c -- client main loop
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "client.h"
28 #if defined WIN32_VARIANT
29 #include <winsock.h>
30 #endif
31
32 #if defined UNIX_VARIANT
33 #if defined HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #include <sys/socket.h>
37 #include <sys/time.h>
38 #include <netinet/in.h>
39 #include <netdb.h>
40 #include <sys/param.h>
41 #include <sys/ioctl.h>
42 #include <sys/uio.h>
43 #include <errno.h>
44 #endif
45
46 #if defined HAVE_PUTENV && !defined HAVE__PUTENV
47 #define _putenv putenv
48 #endif
49
50
51 cvar_t *freelook;
52
53 cvar_t *adr0;
54 cvar_t *adr1;
55 cvar_t *adr2;
56 cvar_t *adr3;
57 cvar_t *adr4;
58 cvar_t *adr5;
59 cvar_t *adr6;
60 cvar_t *adr7;
61 cvar_t *adr8;
62
63 cvar_t *cl_stereo_separation;
64 cvar_t *cl_stereo;
65
66 cvar_t *rcon_client_password;
67 cvar_t *rcon_address;
68
69 cvar_t *cl_noskins;
70 cvar_t *cl_autoskins;
71 cvar_t *cl_footsteps;
72 cvar_t *cl_timeout;
73 cvar_t *cl_predict;
74 cvar_t *cl_maxfps;
75 cvar_t *cl_gun;
76 cvar_t *cl_brass;
77 cvar_t *cl_showPlayerNames;
78 cvar_t *cl_playtaunts;
79 cvar_t *cl_centerprint;
80 cvar_t *cl_precachecustom;
81 cvar_t *cl_simpleitems;
82 cvar_t *cl_flicker;
83
84 cvar_t *cl_paindist;
85 cvar_t *cl_explosiondist;
86 cvar_t *cl_raindist;
87
88 cvar_t *cl_add_particles;
89 cvar_t *cl_add_lights;
90 cvar_t *cl_add_entities;
91 cvar_t *cl_add_blend;
92
93 cvar_t *cl_shownet;
94 cvar_t *cl_showmiss;
95 cvar_t *cl_showclamp;
96
97 cvar_t *cl_paused;
98 cvar_t *cl_timedemo;
99 cvar_t *cl_demoquit;
100
101 cvar_t *lookspring;
102 cvar_t *lookstrafe;
103 cvar_t *sensitivity;
104 cvar_t *menu_sensitivity;
105
106 cvar_t *m_smoothing;
107 cvar_t *m_pitch;
108 cvar_t *m_yaw;
109 cvar_t *m_forward;
110 cvar_t *m_side;
111
112 cvar_t *cl_test;
113
114 //
115 // userinfo
116 //
117 cvar_t *info_password;
118 cvar_t *info_spectator;
119 cvar_t *info_spectator_password;
120 cvar_t *name;
121 cvar_t *stats_password;
122 cvar_t *pw_hashed;
123 cvar_t *skin;
124 cvar_t *rate;
125 cvar_t *fov;
126 cvar_t *msg;
127 cvar_t *hand;
128 cvar_t *gender;
129 cvar_t *gender_auto;
130
131 cvar_t *cl_vwep;
132 cvar_t *cl_vehicle_huds;
133
134 cvar_t *background_music;
135 cvar_t *background_music_vol;
136
137 cvar_t *scriptsloaded;
138
139 //master server
140 cvar_t *cl_master;
141 cvar_t *cl_master2;
142
143 //custom huds
144 cvar_t *cl_hudimage1;
145 cvar_t *cl_hudimage2;
146
147 //health aura
148 cvar_t *cl_healthaura;
149
150 //blood
151 cvar_t *cl_noblood;
152
153 //beam color for disruptor
154 cvar_t *cl_disbeamclr;
155
156 cvar_t *cl_dmlights;
157
158 //Stats
159 cvar_t *cl_stats_server;
160
161 //latest version of the game available
162 cvar_t *cl_latest_game_version_url;
163
164 cvar_t *cl_speedrecord;
165 cvar_t *cl_alltimespeedrecord;
166
167 client_static_t cls;
168 client_state_t cl;
169
170 centity_t cl_entities[MAX_EDICTS];
171
172 entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
173
174 extern cvar_t *allow_download;
175 extern cvar_t *allow_download_players;
176 extern cvar_t *allow_download_models;
177 extern cvar_t *allow_download_sounds;
178 extern cvar_t *allow_download_maps;
179
180 #if defined WIN32_VARIANT
181 extern char map_music[MAX_PATH];
182 extern char map_music_sec[MAX_PATH];
183 #else
184 extern char map_music[MAX_OSPATH];
185 extern char map_music_sec[MAX_OSPATH];
186 #endif
187
188 extern void RS_FreeAllScripts(void);
189
190 typedef struct _PLAYERINFO {
191 char playername[PLAYERNAME_SIZE];
192 int ping;
193 int score;
194 } PLAYERINFO;
195
196 typedef struct _SERVERINFO {
197 char ip[32];
198 unsigned short port;
199 char szHostName[256];
200 char szMapName[256];
201 int curClients;
202 int maxClients;
203 int starttime;
204 int ping;
205 PLAYERINFO players[64];
206 int temporary;
207 } SERVERINFO;
208
209 SERVERINFO servers[64];
210 static int numServers = 0;
211
212 static size_t szr; // just for unused result warnings
213
214 //
215 // Fonts
216 //
217 FNT_auto_t CL_gameFont;
218 static struct FNT_auto_s _CL_gameFont;
219 FNT_auto_t CL_centerFont;
220 static struct FNT_auto_s _CL_centerFont;
221 FNT_auto_t CL_consoleFont;
222 static struct FNT_auto_s _CL_consoleFont;
223 FNT_auto_t CL_menuFont;
224 static struct FNT_auto_s _CL_menuFont;
225
226
227 //======================================================================
228
229
230 /*
231 ====================
232 CL_WriteDemoMessage
233
234 Dumps the current net message, prefixed by the length
235 ====================
236 */
CL_WriteDemoMessage(void)237 void CL_WriteDemoMessage (void)
238 {
239 int len, swlen;
240
241 // the first eight bytes are just packet sequencing stuff
242 len = net_message.cursize-8;
243 swlen = LittleLong(len);
244 if (!swlen)
245 return;
246
247 szr = fwrite (&swlen, 4, 1, cls.demofile);
248 szr = fwrite (net_message.data+8, len, 1, cls.demofile);
249 }
250
251
252 /*
253 ====================
254 CL_Stop_f
255
256 stop recording a demo
257 ====================
258 */
CL_Stop_f(void)259 void CL_Stop_f (void)
260 {
261 int len;
262
263 if (!cls.demorecording)
264 {
265 Com_Printf ("Not recording a demo.\n");
266 return;
267 }
268
269 // finish up
270 len = -1;
271 szr = fwrite (&len, 4, 1, cls.demofile);
272 fclose (cls.demofile);
273 cls.demofile = NULL;
274 cls.demorecording = false;
275 Com_Printf ("Stopped demo.\n");
276 }
277
278 /*
279 ====================
280 CL_Record_f
281
282 record <demoname>
283
284 Begins recording a demo from the current position
285 ====================
286 */
CL_Record_f(void)287 void CL_Record_f (void)
288 {
289 char name[MAX_OSPATH];
290 char buf_data[MAX_MSGLEN];
291 sizebuf_t buf;
292 int i;
293 int len;
294 entity_state_t *ent;
295 entity_state_t nullstate;
296
297 if (Cmd_Argc() != 2)
298 {
299 Com_Printf ("record <demoname>\n");
300 return;
301 }
302
303 if (cls.demorecording)
304 {
305 Com_Printf ("Already recording.\n");
306 return;
307 }
308
309 if (cls.state != ca_active)
310 {
311 Com_Printf ("You must be in a level to record.\n");
312 return;
313 }
314
315 //
316 // open the demo file
317 //
318 Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
319
320 Com_Printf ("recording to %s.\n", name);
321 FS_CreatePath (name);
322 cls.demofile = fopen (name, "wb");
323 if (!cls.demofile)
324 {
325 Com_Printf ("ERROR: couldn't open.\n");
326 return;
327 }
328 cls.demorecording = true;
329
330 // don't start saving messages until a non-delta compressed message is received
331 cls.demowaiting = true;
332
333 //
334 // write out messages to hold the startup information
335 //
336 SZ_Init (&buf, (byte *)buf_data, sizeof(buf_data));
337 SZ_SetName ( &buf, "CL_Record_f", false );
338
339 // send the serverdata
340 MSG_WriteByte (&buf, svc_serverdata);
341 MSG_WriteLong (&buf, PROTOCOL_VERSION);
342 MSG_WriteLong (&buf, 0x10000 + cl.servercount);
343 MSG_WriteByte (&buf, 1); // demos are always attract loops
344 MSG_WriteString (&buf, cl.gamedir);
345 MSG_WriteShort (&buf, cl.playernum);
346
347 MSG_WriteString (&buf, cl.configstrings[CS_NAME]);
348
349 // configstrings
350 for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
351 {
352 if (cl.configstrings[i][0])
353 {
354 if (buf.cursize + strlen (cl.configstrings[i]) + 32 > buf.maxsize)
355 { // write it out
356 len = LittleLong (buf.cursize);
357 szr = fwrite (&len, 4, 1, cls.demofile);
358 szr = fwrite (buf.data, buf.cursize, 1, cls.demofile);
359 buf.cursize = 0;
360 }
361
362 MSG_WriteByte (&buf, svc_configstring);
363 MSG_WriteShort (&buf, i);
364 MSG_WriteString (&buf, cl.configstrings[i]);
365 }
366
367 }
368
369 // baselines
370 memset (&nullstate, 0, sizeof(nullstate));
371 for (i=0; i<MAX_EDICTS ; i++)
372 {
373 ent = &cl_entities[i].baseline;
374 if (!ent->modelindex)
375 continue;
376
377 if (buf.cursize + 64 > buf.maxsize)
378 { // write it out
379 len = LittleLong (buf.cursize);
380 szr = fwrite (&len, 4, 1, cls.demofile);
381 szr = fwrite (buf.data, buf.cursize, 1, cls.demofile);
382 buf.cursize = 0;
383 }
384
385 MSG_WriteByte (&buf, svc_spawnbaseline);
386 MSG_WriteDeltaEntity (&nullstate, &cl_entities[i].baseline, &buf, true, true);
387 }
388
389 MSG_WriteByte (&buf, svc_stufftext);
390 MSG_WriteString (&buf, "precache\n");
391
392 // write it to the demo file
393
394 len = LittleLong (buf.cursize);
395 szr = fwrite (&len, 4, 1, cls.demofile);
396 szr = fwrite (buf.data, buf.cursize, 1, cls.demofile);
397
398 // the rest of the demo file will be individual frames
399 }
400
401 //======================================================================
402
403
404
405 /*
406 ===================
407 Cmd_ForwardToServer
408
409 adds the current command line as a clc_stringcmd to the client message.
410 things like godmode, noclip, etc, are commands directed to the server,
411 so when they are typed in at the console, they will need to be forwarded.
412 ===================
413 */
Cmd_ForwardToServer(void)414 void Cmd_ForwardToServer (void)
415 {
416 char *cmd;
417
418 cmd = Cmd_Argv(0);
419 if (cls.state <= ca_connected || *cmd == '-' || *cmd == '+')
420 {
421 Com_Printf ("Unknown command \"%s\"\n", cmd);
422 return;
423 }
424
425 MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
426 SZ_Print (&cls.netchan.message, cmd);
427 if (Cmd_Argc() > 1)
428 {
429 SZ_Print (&cls.netchan.message, " ");
430 SZ_Print (&cls.netchan.message, Cmd_Args());
431 }
432 }
433
CL_Setenv_f(void)434 void CL_Setenv_f( void )
435 {
436 int argc = Cmd_Argc();
437
438 if ( argc > 2 )
439 {
440 char buffer[1000];
441 int i;
442
443 strcpy( buffer, Cmd_Argv(1) );
444 strcat( buffer, "=" );
445
446 for ( i = 2; i < argc; i++ )
447 {
448 strcat( buffer, Cmd_Argv( i ) );
449 strcat( buffer, " " );
450 }
451
452 _putenv( buffer );
453 }
454 else if ( argc == 2 )
455 {
456 char *env = getenv( Cmd_Argv(1) );
457
458 if ( env )
459 {
460 Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
461 }
462 else
463 {
464 Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
465 }
466 }
467 }
468
469
470 /*
471 ==================
472 CL_ForwardToServer_f
473 ==================
474 */
CL_ForwardToServer_f(void)475 void CL_ForwardToServer_f (void)
476 {
477 if (cls.state != ca_connected && cls.state != ca_active)
478 {
479 Com_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
480 return;
481 }
482
483 // don't forward the first argument
484 if (Cmd_Argc() > 1)
485 {
486 MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
487 SZ_Print (&cls.netchan.message, Cmd_Args());
488 }
489 }
490
491
492 /*
493 ==================
494 CL_Pause_f
495 ==================
496 */
CL_Pause_f(void)497 void CL_Pause_f (void)
498 {
499 // never pause in multiplayer
500 if (Cvar_VariableValue ("maxclients") > 1 || !Com_ServerState ())
501 {
502 Cvar_SetValue ("paused", 0);
503 return;
504 }
505
506 Cvar_SetValue ("paused", !cl_paused->integer);
507 }
508
509 /*
510 ==================
511 CL_Quit_f
512 ==================
513 */
CL_Quit_f(void)514 void CL_Quit_f (void)
515 {
516 S_StopAllSounds ();
517 CL_Disconnect ();
518 Com_Quit ();
519 }
520
521 /*
522 ================
523 CL_Drop
524
525 Called after an ERR_DROP was thrown
526 ================
527 */
CL_Drop(void)528 void CL_Drop (void)
529 {
530 if (cls.state == ca_uninitialized)
531 return;
532 if (cls.state != ca_disconnected)
533 CL_Disconnect ();
534
535 // drop loading plaque unless this is the initial game start
536 if (cls.disable_servercount != -1)
537 SCR_EndLoadingPlaque (); // get rid of loading plaque
538 }
539
540
541 /*
542 =======================
543 CL_SendConnectPacket
544
545 We have gotten a challenge from the server, so try and
546 connect.
547 ======================
548 */
CL_SendConnectPacket(void)549 void CL_SendConnectPacket (void)
550 {
551 netadr_t adr;
552 int port;
553
554 if (!NET_StringToAdr (cls.servername, &adr))
555 {
556 Com_Printf ("Bad server address\n");
557 cls.connect_time = 0;
558 return;
559 }
560 if (adr.port == 0)
561 adr.port = BigShort (PORT_SERVER);
562
563 port = Cvar_VariableValue ("qport");
564 userinfo_modified = false;
565
566 Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n",
567 PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() );
568 }
569
570 /*
571 =================
572 CL_CheckForResend
573
574 Resend a connect message if the last one has timed out
575 =================
576 */
CL_CheckForResend(void)577 void CL_CheckForResend (void)
578 {
579 netadr_t adr;
580
581 // if the local server is running and we aren't
582 // then connect
583 if (cls.state == ca_disconnected && Com_ServerState() )
584 {
585 cls.state = ca_connecting;
586 strncpy (cls.servername, "localhost", sizeof(cls.servername)-1);
587 // we don't need a challenge on the localhost
588 CL_SendConnectPacket ();
589 return;
590 // cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
591 }
592
593 // resend if we haven't gotten a reply yet
594 if (cls.state != ca_connecting)
595 return;
596
597 if (cls.realtime - cls.connect_time < 3000)
598 return;
599
600 if (!NET_StringToAdr (cls.servername, &adr))
601 {
602 Com_Printf ("Bad server address\n");
603 cls.state = ca_disconnected;
604 return;
605 }
606 if (adr.port == 0)
607 adr.port = BigShort (PORT_SERVER);
608
609 cls.connect_time = cls.realtime; // for retransmit requests
610
611 Com_Printf ("Connecting to %s...\n", cls.servername);
612
613 Netchan_OutOfBandPrint (NS_CLIENT, adr, "getchallenge\n");
614 }
615
616
617 /*
618 ================
619 CL_Connect_f
620
621 ================
622 */
CL_Connect_f(void)623 void CL_Connect_f (void)
624 {
625 char *server;
626
627 if (Cmd_Argc() != 2)
628 {
629 Com_Printf ("usage: connect <server>\n");
630 return;
631 }
632
633 if (Com_ServerState ())
634 { // if running a local server, kill it and reissue
635 SV_Shutdown (va("Server quit\n", msg), false);
636 }
637 else
638 {
639 CL_Disconnect ();
640 }
641
642 server = Cmd_Argv (1);
643
644 NET_Config (true); // allow remote
645
646 CL_Disconnect ();
647
648 cls.state = ca_connecting;
649 strncpy (cls.servername, server, sizeof(cls.servername)-1);
650 cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
651
652 STATS_RequestVerification();
653 }
654
655
656 /*
657 =====================
658 CL_Rcon_f
659
660 Send the rest of the command line over as
661 an unconnected command.
662 =====================
663 */
CL_Rcon_f(void)664 void CL_Rcon_f (void)
665 {
666 char message[1024];
667 sizebuf_t buffer;
668 int i;
669 netadr_t to;
670
671 if ( !(rcon_client_password->string && rcon_client_password->string[0]) && Cmd_Argc() < 3)
672 {
673 Com_Printf ("You must set 'rcon_password' before issuing an rcon command.\n");
674 return;
675 }
676
677 NET_Config (true); // allow remote
678
679 SZ_Init( &buffer, (byte *) message, 1024 );
680 buffer.allowoverflow = true;
681 SZ_SetName( &buffer, "RCon buffer", false );
682
683 SZ_Print (&buffer, "\xff\xff\xff\xffrcon ");
684 if ( rcon_client_password->string && rcon_client_password->string[0] )
685 {
686 SZ_Print (&buffer, "\"");
687 SZ_Print (&buffer, rcon_client_password->string);
688 SZ_Print (&buffer, "\" ");
689 }
690
691 for (i=1 ; i<Cmd_Argc() ; i++)
692 {
693 SZ_Print (&buffer, "\"");
694 SZ_Print (&buffer, Cmd_Argv(i));
695 SZ_Print (&buffer, "\" ");
696 }
697
698 if ( buffer.overflowed )
699 {
700 Com_Printf ("Rcon command too long\n");
701 return;
702 }
703
704 if (cls.state >= ca_connected)
705 to = cls.netchan.remote_address;
706 else
707 {
708 if (!strlen(rcon_address->string))
709 {
710 Com_Printf ("You must either be connected, or set the 'rcon_address' cvar to issue rcon commands\n");
711
712 return;
713 }
714 NET_StringToAdr (rcon_address->string, &to);
715 if (to.port == 0)
716 to.port = BigShort (PORT_SERVER);
717 }
718
719 NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
720 }
721
722
723 /*
724 =====================
725 CL_ClearState
726
727 =====================
728 */
CL_ClearState(void)729 void CL_ClearState (void)
730 {
731 S_StopAllSounds ();
732 CL_ClearEffects ();
733 CL_ClearTEnts ();
734
735 // wipe the entire cl structure
736 memset (&cl, 0, sizeof(cl));
737 memset (cl_entities, 0, sizeof(cl_entities));
738
739 SZ_Clear (&cls.netchan.message);
740
741 }
742
743 /*
744 =====================
745 CL_Disconnect
746
747 Goes from a connected state to full screen console state
748 Sends a disconnect message to the server
749 This is also called on Com_Error, so it shouldn't cause any errors
750 =====================
751 */
CL_Disconnect(void)752 void CL_Disconnect (void)
753 {
754 byte final[32];
755 int repeat;
756
757 if (cls.state == ca_disconnected)
758 return;
759
760 if (cl_timedemo && cl_timedemo->integer)
761 {
762 int time;
763
764 time = Sys_Milliseconds () - cl.timedemo_start;
765 if (time > 0)
766 Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames,
767 time/1000.0, cl.timedemo_frames*1000.0 / time);
768 cl.timedemo_start = 0;
769 }
770
771 VectorClear (cl.refdef.blend);
772 R_SetPalette(NULL);
773
774 M_ForceMenuOff ();
775
776 cls.connect_time = 0;
777
778 if (cls.demorecording)
779 CL_Stop_f ();
780
781 // send a disconnect message to the server
782 final[0] = clc_stringcmd;
783 strcpy ((char *)final+1, "disconnect");
784 for ( repeat = 3 ; repeat-- ;)
785 Netchan_Transmit( &cls.netchan, (int)strlen( (char*)final ), final );
786
787 CL_ClearState ();
788
789 // stop download
790 if(cls.download){
791 if(cls.downloadhttp) // clean up http downloads
792 CL_HttpDownloadCleanup();
793 else // or just stop legacy ones
794 fclose(cls.download);
795 cls.download = NULL;
796 }
797
798 cls.state = ca_disconnected;
799
800 if (cl_demoquit && cl_demoquit->integer &&
801 cl_timedemo && cl_timedemo->integer) {
802 CL_Quit_f();
803 }
804 }
805
CL_Disconnect_f(void)806 void CL_Disconnect_f (void)
807 {
808 Com_Error (ERR_DROP, "Disconnected from server");
809 }
810
811
812 /*
813 =================
814 CL_Changing_f
815
816 Just sent as a hint to the client that they should
817 drop to full console
818 =================
819 */
CL_Changing_f(void)820 void CL_Changing_f (void)
821 {
822 //ZOID
823 //if we are downloading, we don't change! This so we don't suddenly stop downloading a map
824 if (cls.download)
825 return;
826
827 SCR_BeginLoadingPlaque ();
828 cls.state = ca_connected; // not active anymore, but not disconnected
829 Com_Printf ("\nChanging map...\n");
830 }
831
832
833 /*
834 =================
835 CL_Reconnect_f
836
837 The server is changing levels
838 =================
839 */
CL_Reconnect_f(void)840 void CL_Reconnect_f (void)
841 {
842 //ZOID
843 //if we are downloading, we don't change! This so we don't suddenly stop downloading a map
844 if (cls.download)
845 return;
846
847 S_StopAllSounds ();
848 if (cls.state == ca_connected) {
849 Com_Printf ("reconnecting...\n");
850 cls.state = ca_connected;
851 MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
852 MSG_WriteString (&cls.netchan.message, "new");
853 return;
854 }
855
856 if (*cls.servername) {
857 if (cls.state >= ca_connected) {
858 CL_Disconnect();
859 cls.connect_time = cls.realtime - 1500;
860 } else
861 cls.connect_time = -99999; // fire immediately
862
863 cls.state = ca_connecting;
864 Com_Printf ("reconnecting...\n");
865 }
866 }
867
868 /*
869 =================
870 CL_ParseStatusMessage
871
872 Handle a reply from a status request
873 =================
874 */
CL_ParseFullStatusMessage(void)875 void CL_ParseFullStatusMessage (void)
876 {
877 char *s;
878
879 s = MSG_ReadString(&net_message);
880
881 Com_Printf ("TESTING!!!!%s\n", s);
882 M_AddToServerList (net_from, s);
883 }
884
885 /*
886 ===================
887 GetServerList
888
889 Get a list of servers from the master
890 ===================
891 */
GetServerList(void)892 static void GetServerList (void)
893 {
894 char *requeststring;
895 netadr_t adr;
896
897 // this function may block => sound channels might not be updated
898 // while it does so => prevent 'looping sound effect' while waiting
899 // -JR / 20050731 / 2
900 S_StopAllSounds ();
901
902 requeststring = va( "query" );
903
904 // send a broadcast packet
905 Com_Printf( "querying %s...\n", cl_master->string );
906
907 if( NET_StringToAdr( cl_master->string, &adr ) ) {
908 if( !adr.port )
909 adr.port = BigShort( PORT_MASTER );
910 Netchan_OutOfBandPrint( NS_CLIENT, adr, requeststring );
911 }
912 else
913 {
914 Com_Printf( "Bad address: %s\n", cl_master->string);
915 }
916
917 // Query secondary server
918 Com_Printf( "querying %s...\n", cl_master2->string );
919 if ( NET_StringToAdr( cl_master2->string, &adr ) ) {
920 if ( !adr.port )
921 adr.port = BigShort( PORT_MASTER );
922 Netchan_OutOfBandPrint( NS_CLIENT, adr, requeststring );
923 }
924 else
925 {
926 Com_Printf( "Bad address: %s\n", cl_master2->string);
927 }
928
929 // Tactical mode to do: Query third server - tactical servers will only reside on this server
930
931 // Reset server list for incoming responses.
932 numServers = 0;
933 }
934 /*
935 =================
936 CL_ParseGetServersResponse
937
938 Handle a reply from getservers message to master server
939 =================
940 */
CL_ParseGetServersResponse()941 static void CL_ParseGetServersResponse()
942 {
943 cvar_t *noudp;
944 cvar_t *noipx;
945 netadr_t adr;
946 char adrString[32];
947 byte addr[4];
948 byte port[2];
949 int idx;
950 unsigned short uport;
951 qboolean dupe;
952
953 MSG_BeginReading (&net_message);
954 MSG_ReadLong (&net_message); // skip the -1
955
956 noudp = Cvar_Get ("noudp", "0", CVAR_NOSET);
957 if (!noudp->integer)
958 {
959 adr.type = NA_BROADCAST;
960 }
961 noipx = Cvar_Get ("noipx", "0", CVAR_NOSET);
962 if (!noipx->integer)
963 {
964 adr.type = NA_BROADCAST_IPX;
965 }
966
967 if ( net_message.readcount + 8 < net_message.cursize )
968 net_message.readcount += 8;
969
970 while( net_message.readcount +6 <= net_message.cursize )
971 {
972 if (numServers == 64)
973 break; // No room left.
974
975 MSG_ReadData( &net_message, addr, 4 );
976 Com_sprintf( adrString, sizeof( adrString ), "%u.%u.%u.%u",
977 addr[0], addr[1], addr[2], addr[3]);
978 MSG_ReadData( &net_message, port, 2 ); /* network byte order (bigendian) */
979 /* convert to unsigned short in host byte order */
980 uport = (unsigned short) ((port[0] << 8) + port[1]);
981
982 dupe = false;
983 // Check that we don't have a duplicate entry.
984 for (idx = 0; idx < numServers; idx++) {
985 if ( !strcmp(servers[idx].ip, adrString) &&
986 servers[idx].port == uport) {
987 Com_Printf("Already have: %s:%u\n", adrString,
988 uport);
989 dupe = true;
990 break;
991 }
992 }
993 if (dupe)
994 continue;
995
996 memcpy( &servers[numServers].ip, adrString, sizeof(servers[numServers].ip) );
997 servers[numServers].port = uport;
998
999 if (!NET_StringToAdr (servers[numServers].ip, &adr))
1000 {
1001 Com_Printf ("Bad address: %s\n", servers[numServers].ip);
1002 continue;
1003 }
1004
1005 Com_Printf ("pinging %s:%hu...\n",
1006 servers[numServers].ip, servers[numServers].port );
1007
1008 /* adr.port is in network byte order (bigendian) */
1009 adr.port = (unsigned short)BigShort( servers[numServers].port );
1010 if (!adr.port) {
1011 adr.port = BigShort(PORT_SERVER);
1012 }
1013 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("status %i", PROTOCOL_VERSION));
1014 servers[numServers].starttime = Sys_Milliseconds();
1015
1016 ++numServers;
1017 }
1018 }
1019
1020 /*
1021 =================
1022 CL_GetPingStartTime
1023
1024 Return ping starttime for server given its address. Returns 0 if not found.
1025 =================
1026 */
CL_GetPingStartTime(netadr_t adr)1027 int CL_GetPingStartTime(netadr_t adr)
1028 {
1029 int idx;
1030 char adrString[32];
1031 unsigned short port;
1032
1033 Com_sprintf(adrString, sizeof(adrString), "%u.%u.%u.%u",
1034 adr.ip[0], adr.ip[1], adr.ip[2], adr.ip[3]);
1035 port = (unsigned short) BigShort(adr.port);
1036 for (idx = 0; idx < numServers; idx++) {
1037 if (! strcmp(adrString, servers[idx].ip) &&
1038 port == servers[idx].port) {
1039 return servers[idx].starttime;
1040 }
1041 }
1042 return 0;
1043 }
1044
1045 /*
1046 =================
1047 CL_PingServers_f
1048 =================
1049 */
1050 static void CL_ReadPackets ();
CL_PingServers_f(void)1051 void CL_PingServers_f (void)
1052 {
1053
1054 int i;
1055 netadr_t adr;
1056 char name[32];
1057 char *adrstring;
1058 cvar_t *noudp;
1059 cvar_t *noipx;
1060
1061 NET_Config (true); // allow remote
1062
1063 GetServerList(); //get list from COR master server
1064
1065 // send a broadcast packet
1066 Com_Printf ("pinging broadcast...\n");
1067
1068 noudp = Cvar_Get ("noudp", "0", CVAR_NOSET);
1069 if (!noudp->integer)
1070 {
1071 adr.type = NA_BROADCAST;
1072 adr.port = BigShort(PORT_SERVER);
1073 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("status %i", PROTOCOL_VERSION));
1074
1075 }
1076 noipx = Cvar_Get ("noipx", "0", CVAR_NOSET);
1077 if (!noipx->integer)
1078 {
1079 adr.type = NA_BROADCAST_IPX;
1080 adr.port = BigShort(PORT_SERVER);
1081 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("status %i", PROTOCOL_VERSION));
1082 }
1083
1084 // send a packet to each address book entry
1085 for (i=0 ; i<16 ; i++)
1086 {
1087 Com_sprintf (name, sizeof(name), "adr%i", i);
1088 adrstring = Cvar_VariableString (name);
1089 if (!adrstring || !adrstring[0])
1090 continue;
1091
1092 Com_Printf ("pinging %s...\n", adrstring);
1093 if (!NET_StringToAdr (adrstring, &adr))
1094 {
1095 Com_Printf ("Bad address: %s\n", adrstring);
1096 continue;
1097 }
1098 if (!adr.port)
1099 adr.port = BigShort(PORT_SERVER);
1100
1101 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("status %i", PROTOCOL_VERSION));
1102
1103 }
1104
1105 // Note that all we have done thus far is
1106 // - Request server lists from the two master servers
1107 // - Sent a broadcast to the LAN looking for local servers
1108 // When CL_ReadPackets gets the responses from the master server, it will
1109 // automatically ping all the game servers. Ideally, if the net connection
1110 // is pretty good, we can get that all done in the following loop. The
1111 // game's main loop will catch any stragglers.
1112
1113 // Read packets at 5 ms intervals 60 times. That's 300 ms. Figure maybe
1114 // 100-150 ms round trip to the master server, then another 100-150 ms
1115 // round trip to the game servers. So we should be able to grab any
1116 // servers 150 ping or less and gauge their pings to within 5 ms or so.
1117 #define PING_LOOP_TOTAL_MS 300
1118 #define PING_LOOP_INTERVAL_MS 5
1119 #define PING_LOOP_ITERATIONS (PING_LOOP_TOTAL_MS/PING_LOOP_INTERVAL_MS)
1120 for (i = 0; i < PING_LOOP_ITERATIONS; i++)
1121 {
1122 #if defined UNIX_VARIANT
1123 usleep (PING_LOOP_INTERVAL_MS*1000);
1124 #elif defined WIN32_VARIANT
1125 Sleep (PING_LOOP_INTERVAL_MS);
1126 #else
1127 #warning client/cl_main.c: CL_PingServers_f (): \
1128 Do not know what sleep function to use!
1129 break;
1130 #endif
1131 CL_ReadPackets ();
1132 }
1133
1134 // -JD restart the menu music that was stopped during this procedure
1135 S_StartMenuMusic();
1136 }
1137
1138
1139 /*
1140 =================
1141 CL_Skins_f
1142
1143 Load or download any custom player skins and models
1144 =================
1145 */
CL_Skins_f(void)1146 void CL_Skins_f (void)
1147 {
1148 int i;
1149
1150 for (i=0 ; i<MAX_CLIENTS ; i++)
1151 {
1152 if (!cl.configstrings[CS_PLAYERSKINS+i][0])
1153 continue;
1154 Com_Printf ("client %i: %s\n", i, cl.configstrings[CS_PLAYERSKINS+i]);
1155 SCR_UpdateScreen ();
1156 Sys_SendKeyEvents (); // pump message loop
1157 CL_ParseClientinfo (i);
1158 }
1159 }
1160
1161
1162 /*
1163 =================
1164 CL_ConnectionlessPacket
1165
1166 Responses to broadcasts, etc
1167 =================
1168 */
CL_ConnectionlessPacket(void)1169 static void CL_ConnectionlessPacket (void)
1170 {
1171 char *s;
1172 char *c;
1173
1174 MSG_BeginReading (&net_message);
1175 MSG_ReadLong (&net_message); // skip the -1
1176
1177 s = MSG_ReadStringLine (&net_message);
1178
1179 Cmd_TokenizeString (s, false);
1180
1181 c = Cmd_Argv(0);
1182
1183 Com_Printf ("%s: %s\n", NET_AdrToString (net_from), c);
1184
1185 if(!strncmp(c, "servers", 7))
1186 {
1187 CL_ParseGetServersResponse();
1188 return;
1189 }
1190
1191 if(!strncmp(c, "vstring", 7))
1192 {
1193 s = Cmd_Argv(1);
1194 switch(currLoginState.requestType)
1195 {
1196 case STATSLOGIN:
1197
1198 STATS_AuthenticateStats(s);
1199 break;
1200 case STATSPWCHANGE:
1201 STATS_ChangePassword(s);
1202 break;
1203 }
1204 return;
1205 }
1206
1207 if(!strncmp(c, "validated", 9))
1208 {
1209 //in cases of changed passwords
1210 if(currLoginState.requestType == STATSPWCHANGE)
1211 {
1212 //make sure the password is stored for future use
1213 Q_strncpyz2(currLoginState.old_password, stats_password->string, sizeof(currLoginState.old_password));
1214
1215 Com_Printf("Password change successful!\n");
1216 }
1217 else
1218 {
1219 Com_Printf("Account validated!\n");
1220 currLoginState.validated = true;
1221 }
1222 return;
1223 }
1224
1225 // server connection
1226 if (!strcmp(c, "client_connect"))
1227 {
1228 if (cls.state == ca_connected)
1229 {
1230 Com_Printf ("Dup connect received. Ignored.\n");
1231 return;
1232 }
1233 Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, cls.quakePort);
1234 MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
1235 MSG_WriteString (&cls.netchan.message, "new");
1236 cls.state = ca_connected;
1237
1238 memset(cls.downloadurl, 0, sizeof(cls.downloadurl));
1239 if(Cmd_Argc() == 2) // http download url
1240 {
1241 strncpy(cls.downloadurl, Cmd_Argv(1), sizeof(cls.downloadurl) - 1);
1242 }
1243 is_localhost = !strcmp(cls.servername, "localhost");
1244 Netchan_OutOfBandPrint (NS_CLIENT, net_from, va("status %i", PROTOCOL_VERSION));
1245 return;
1246 }
1247
1248 // remote command from gui front end
1249 if (!strcmp(c, "cmd"))
1250 {
1251 if (!NET_IsLocalAddress(net_from))
1252 {
1253 Com_Printf ("Command packet from remote host. Ignored.\n");
1254 return;
1255 }
1256 Sys_AppActivate ();
1257 s = MSG_ReadString (&net_message);
1258 Cbuf_AddText (s);
1259 Cbuf_AddText ("\n");
1260 return;
1261 }
1262
1263 // print command from somewhere
1264 if (!strcmp(c, "print"))
1265 {
1266 s = MSG_ReadString (&net_message);
1267 if (s[0] == '\\')
1268 {
1269 char *playerinfo_start;
1270 if (cls.state >= ca_connected &&
1271 !memcmp(&net_from, &cls.netchan.remote_address, sizeof(netadr_t)))
1272 M_UpdateConnectedServerInfo (net_from, s);
1273 if (cls.key_dest == key_menu)
1274 {
1275 M_AddToServerList (net_from, s); //add net_from so we have the addy
1276 }
1277 else
1278 {
1279 // If someone called pingservers () directly from the console,
1280 // chances are he wants to read the server list manually
1281 // anyway.
1282 playerinfo_start = strchr (s, '\n');
1283 *playerinfo_start++ = '\0';
1284 Info_Print (s);
1285 Com_Printf ("%s", playerinfo_start);
1286 }
1287 }
1288 else
1289 Com_Printf ("%s", s);
1290 return;
1291 }
1292
1293 // ping from somewhere
1294 if (!strcmp(c, "ping"))
1295 {
1296 Netchan_OutOfBandPrint (NS_CLIENT, net_from, "ack");
1297 return;
1298 }
1299
1300 // challenge from the server we are connecting to
1301 if (!strcmp(c, "challenge"))
1302 {
1303 cls.challenge = atoi(Cmd_Argv(1));
1304 CL_SendConnectPacket ();
1305 return;
1306 }
1307
1308 // echo request from server
1309 if (!strcmp(c, "echo"))
1310 {
1311 Netchan_OutOfBandPrint (NS_CLIENT, net_from, "%s", Cmd_Argv(1) );
1312 return;
1313 }
1314
1315 if (!strcmp(c, "teamgame"))
1316 {
1317 server_is_team = atoi (Cmd_Argv(1));
1318 return;
1319 }
1320
1321 Com_Printf ("Unknown command.\n");
1322 }
1323
1324
1325 /*
1326 =================
1327 CL_DumpPackets
1328
1329 A vain attempt to help bad TCP stacks that cause problems
1330 when they overflow
1331 =================
1332 */
CL_DumpPackets(void)1333 void CL_DumpPackets (void)
1334 {
1335 while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
1336 {
1337 Com_Printf ("dumping a packet\n");
1338 }
1339 }
1340
1341 /*
1342 =================
1343 CL_ReadPackets
1344 =================
1345 */
1346 int c_incoming_bytes = 0;
CL_ReadPackets(void)1347 static void CL_ReadPackets (void)
1348 {
1349 while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
1350 {
1351 c_incoming_bytes += net_message.cursize;
1352
1353 //
1354 // remote command packet
1355 //
1356 if (*(int *)net_message.data == -1)
1357 {
1358 CL_ConnectionlessPacket ();
1359 continue;
1360 }
1361
1362 if (cls.state == ca_disconnected || cls.state == ca_connecting)
1363 continue; // dump it if not connected
1364
1365 if (net_message.cursize < 8)
1366 {
1367 Com_Printf ("%s: Runt packet\n",NET_AdrToString(net_from));
1368 continue;
1369 }
1370
1371 //
1372 // packet from server
1373 //
1374 if (!NET_CompareAdr (net_from, cls.netchan.remote_address))
1375 {
1376 Com_DPrintf ("%s:sequenced packet without connection\n"
1377 ,NET_AdrToString(net_from));
1378 continue;
1379 }
1380 if (!Netchan_Process(&cls.netchan, &net_message))
1381 continue; // wasn't accepted for some reason
1382 CL_ParseServerMessage ();
1383 }
1384
1385 //
1386 // check timeout
1387 //
1388 if (cls.state >= ca_connected
1389 && cls.realtime - cls.netchan.last_received > cl_timeout->value*1000)
1390 {
1391 if (++cl.timeoutcount > 5) // timeoutcount saves debugger
1392 {
1393 Com_Printf ("\nServer connection timed out.\n");
1394 CL_Disconnect ();
1395 return;
1396 }
1397 }
1398 else
1399 cl.timeoutcount = 0;
1400
1401 }
1402
1403
1404 //=============================================================================
1405
1406 /*
1407 ==============
1408 CL_FixUpGender_f
1409 ==============
1410 */
CL_FixUpGender(void)1411 void CL_FixUpGender(void)
1412 {
1413 char *p;
1414 char sk[80];
1415
1416 if (gender_auto->integer)
1417 {
1418 if (gender->modified)
1419 {
1420 // was set directly, don't override the user
1421 gender->modified = false;
1422 return;
1423 }
1424
1425 strncpy(sk, skin->string, sizeof(sk) - 2);
1426 sk[sizeof(sk) - 1] = '\0'; // in case skin->string is 79 chars or more
1427 if ((p = strchr(sk, '/')) != NULL)
1428 *p = 0;
1429 if (Q_strcasecmp(sk, "male") == 0 || Q_strcasecmp(sk, "cyborg") == 0)
1430 Cvar_Set ("gender", "male");
1431 else if (Q_strcasecmp(sk, "female") == 0 || Q_strcasecmp(sk, "crackhor") == 0)
1432 Cvar_Set ("gender", "female");
1433 else
1434 Cvar_Set ("gender", "none");
1435 gender->modified = false;
1436 }
1437 }
1438
1439 /*
1440 ==============
1441 CL_Userinfo_f
1442 ==============
1443 */
CL_Userinfo_f(void)1444 void CL_Userinfo_f (void)
1445 {
1446 Com_Printf ("User info settings:\n");
1447 Info_Print (Cvar_Userinfo());
1448 }
1449
1450 /*
1451 =================
1452 CL_Snd_Restart_f
1453
1454 Restart the sound subsystem so it can pick up
1455 new parameters and flush all sounds
1456 =================
1457 */
CL_Snd_Restart_f(void)1458 void CL_Snd_Restart_f (void)
1459 {
1460 S_Shutdown ();
1461 S_Init ();
1462 CL_RegisterSounds ();
1463 }
1464
1465 int precache_check; // for autodownload of precache items
1466 int precache_spawncount;
1467 int precache_tex;
1468 int precache_model_skin;
1469
1470 byte *precache_model; // used for skin checking in alias models
1471
1472 #define PLAYER_MULT 5
1473
1474 // ENV_CNT is map load, ENV_CNT+1 is first env map
1475 #define ENV_CNT (CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
1476 #define TEXTURE_CNT (ENV_CNT+13)
1477 #define SCRIPT_CNT (TEXTURE_CNT+999)
1478
1479 static const char *env_suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
1480
CL_RequestNextDownload(void)1481 void CL_RequestNextDownload (void)
1482 {
1483 unsigned map_checksum; // for detecting cheater maps
1484 char fn[MAX_OSPATH];
1485 char map[MAX_OSPATH];
1486 char script[MAX_OSPATH];
1487 dmdl_t *pheader;
1488 int i, j;
1489
1490 if (cls.state != ca_connected)
1491 return;
1492
1493 if (!allow_download->integer && precache_check < ENV_CNT)
1494 precache_check = ENV_CNT;
1495
1496 if (precache_check == CS_MODELS) // confirm map
1497 {
1498 precache_check = CS_MODELS+2; // 0 isn't used
1499 if (allow_download_maps->integer)
1500 if (!CL_CheckOrDownloadFile(cl.configstrings[CS_MODELS+1]))
1501 return; // started a download
1502 }
1503
1504 redoSkins:
1505 if (precache_check >= CS_MODELS && precache_check < CS_MODELS+MAX_MODELS)
1506 {
1507 if (allow_download_models->integer)
1508 {
1509 while (precache_check < CS_MODELS+MAX_MODELS &&
1510 cl.configstrings[precache_check][0])
1511 {
1512 if (cl.configstrings[precache_check][0] == '*' ||
1513 cl.configstrings[precache_check][0] == '#')
1514 {
1515 precache_check++;
1516 continue;
1517 }
1518 //do not download player models from this section
1519 else if (cl.configstrings[precache_check][0] == 'p' &&
1520 cl.configstrings[precache_check][1] == 'l' &&
1521 cl.configstrings[precache_check][2] == 'a' &&
1522 cl.configstrings[precache_check][3] == 'y' &&
1523 cl.configstrings[precache_check][4] == 'e' &&
1524 cl.configstrings[precache_check][5] == 'r' &&
1525 cl.configstrings[precache_check][6] == 's')
1526 {
1527 precache_check++;
1528 continue;
1529 }
1530 if (precache_model_skin == 0)
1531 {
1532 if (!CL_CheckOrDownloadFile(cl.configstrings[precache_check]))
1533 {
1534 precache_model_skin = 1;
1535 return; // started a download
1536 }
1537 precache_model_skin = 1;
1538 }
1539
1540 // checking for skins in the model
1541 if (!precache_model)
1542 {
1543 FS_LoadFile (cl.configstrings[precache_check], (void **)&precache_model);
1544 if (!precache_model)
1545 {
1546 precache_model_skin = 0;
1547 precache_check++;
1548 continue; // couldn't load it
1549 }
1550 if (LittleLong(*(unsigned *)precache_model) != IDALIASHEADER)
1551 {
1552 // not an alias model
1553 FS_FreeFile(precache_model);
1554 precache_model = 0;
1555 precache_model_skin = 0;
1556 precache_check++;
1557 continue;
1558 }
1559 pheader = (dmdl_t *)precache_model;
1560 if (LittleLong (pheader->version) != ALIAS_VERSION)
1561 {
1562 precache_check++;
1563 precache_model_skin = 0;
1564 continue; // couldn't load it
1565 }
1566 }
1567
1568 pheader = (dmdl_t *)precache_model;
1569
1570 while (precache_model_skin - 1 < LittleLong(pheader->num_skins))
1571 {
1572 if (!CL_CheckOrDownloadFile((char *)precache_model +
1573 LittleLong(pheader->ofs_skins) +
1574 (precache_model_skin - 1)*MAX_SKINNAME))
1575 {
1576 precache_model_skin++;
1577 return; // started a download
1578 }
1579 precache_model_skin++;
1580 }
1581 if (precache_model)
1582 {
1583 FS_FreeFile(precache_model);
1584 precache_model = 0;
1585 }
1586 precache_model_skin = 0;
1587 precache_check++;
1588 }
1589 }
1590 if (precache_model)
1591 {
1592 precache_check = CS_MODELS + 2;
1593 precache_model_skin = 0;
1594
1595 goto redoSkins;
1596 }
1597 precache_check = CS_SOUNDS;
1598 }
1599
1600 if (precache_check >= CS_SOUNDS && precache_check < CS_SOUNDS+MAX_SOUNDS)
1601 {
1602 if (allow_download_sounds->integer)
1603 {
1604 if (precache_check == CS_SOUNDS)
1605 precache_check++; // zero is blank
1606 while (precache_check < CS_SOUNDS+MAX_SOUNDS &&
1607 cl.configstrings[precache_check][0])
1608 {
1609 if (cl.configstrings[precache_check][0] == '*')
1610 {
1611 precache_check++;
1612 continue;
1613 }
1614 Com_sprintf(fn, sizeof(fn), "sound/%s", cl.configstrings[precache_check++]);
1615 if (!CL_CheckOrDownloadFile(fn))
1616 return; // started a download
1617 }
1618 }
1619 precache_check = CS_IMAGES;
1620 }
1621 if (precache_check >= CS_IMAGES && precache_check < CS_IMAGES+MAX_IMAGES)
1622 {
1623 if (precache_check == CS_IMAGES)
1624 precache_check++; // zero is blank
1625 while (precache_check < CS_IMAGES+MAX_IMAGES &&
1626 cl.configstrings[precache_check][0])
1627 {
1628 Com_sprintf(fn, sizeof(fn), "pics/%s.tga", cl.configstrings[precache_check++]);
1629 if (!CL_CheckOrDownloadFile(fn))
1630 return; // started a download
1631 }
1632 precache_check = CS_PLAYERSKINS;
1633 }
1634 if (precache_check >= CS_PLAYERSKINS && precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
1635 {
1636 if (allow_download_players->integer)
1637 {
1638 while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
1639 {
1640 int i, n;
1641 char model[MAX_QPATH], skin[MAX_QPATH], *p;
1642
1643 i = (precache_check - CS_PLAYERSKINS)/PLAYER_MULT;
1644 n = (precache_check - CS_PLAYERSKINS)%PLAYER_MULT;
1645
1646 if (!cl.configstrings[CS_PLAYERSKINS+i][0])
1647 {
1648 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
1649 continue;
1650 }
1651
1652 if ((p = strchr(cl.configstrings[CS_PLAYERSKINS+i], '\\')) != NULL)
1653 p++;
1654 else
1655 p = cl.configstrings[CS_PLAYERSKINS+i];
1656 strcpy(model, p);
1657 p = strchr(model, '/');
1658 if (!p)
1659 p = strchr(model, '\\');
1660 if (p)
1661 {
1662 *p++ = 0;
1663 strcpy(skin, p);
1664 } else
1665 *skin = 0;
1666
1667 switch (n)
1668 {
1669 case 0: // model
1670 Com_sprintf(fn, sizeof(fn), "players/%s/tris.iqm", model);
1671 if (!CL_CheckOrDownloadFile(fn))
1672 {
1673 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1;
1674 return; // started a download
1675 }
1676 n++;
1677 /*FALL THROUGH*/
1678
1679 case 1: // weapon model
1680 Com_sprintf(fn, sizeof(fn), "players/%s/weapon.iqm", model);
1681 if (!CL_CheckOrDownloadFile(fn)) {
1682 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2;
1683 return; // started a download
1684 }
1685 n++;
1686 /*FALL THROUGH*/
1687
1688 case 2: // weapon skin
1689 Com_sprintf(fn, sizeof(fn), "players/%s/weapon.jpg", model);
1690 if (!CL_CheckOrDownloadFile(fn))
1691 {
1692 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3;
1693 return; // started a download
1694 }
1695 n++;
1696 /*FALL THROUGH*/
1697
1698 case 3: // skin
1699 Com_sprintf(fn, sizeof(fn), "players/%s/%s.jpg", model, skin);
1700 if (!CL_CheckOrDownloadFile(fn))
1701 {
1702 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4;
1703 return; // started a download
1704 }
1705 n++;
1706 /*FALL THROUGH*/
1707
1708 case 4: // skin_i
1709 Com_sprintf(fn, sizeof(fn), "players/%s/%s_i.jpg", model, skin);
1710 if (!CL_CheckOrDownloadFile(fn))
1711 {
1712 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5;
1713 return; // started a download
1714 }
1715 // move on to next model
1716 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
1717 }
1718 }
1719 }
1720 // precache phase completed
1721 precache_check = ENV_CNT;
1722 }
1723
1724 if (precache_check == ENV_CNT)
1725 {
1726 precache_check = ENV_CNT + 1;
1727
1728 //FIXME: support for new map specification files
1729 CM_LoadBSP (cl.configstrings[CS_MODELS+1], true, &map_checksum);
1730
1731 if (map_checksum != atoi(cl.configstrings[CS_MAPCHECKSUM]))
1732 {
1733 Com_Error (ERR_DROP, "Local map version differs from server: %i != '%s'\n",
1734 map_checksum, cl.configstrings[CS_MAPCHECKSUM]);
1735 return;
1736 }
1737 }
1738
1739 if (precache_check > ENV_CNT && precache_check < TEXTURE_CNT)
1740 {
1741 if (allow_download->integer && allow_download_maps->integer)
1742 {
1743 while (precache_check < TEXTURE_CNT)
1744 {
1745 int n = precache_check++ - ENV_CNT - 1;
1746
1747 Com_sprintf(fn, sizeof(fn), "env/%s%s.tga",
1748 cl.configstrings[CS_SKY], env_suf[n/2]);
1749 if (!CL_CheckOrDownloadFile(fn))
1750 return; // started a download
1751 }
1752 }
1753 precache_check = TEXTURE_CNT;
1754 }
1755 if (precache_check == TEXTURE_CNT)
1756 {
1757 precache_check = TEXTURE_CNT+1;
1758 precache_tex = 0;
1759 }
1760
1761 // confirm existance of textures, download any that don't exist
1762 if (precache_check == TEXTURE_CNT+1)
1763 {
1764 // from qcommon/cmodel.c
1765 extern int numtexinfo;
1766 extern mapsurface_t map_surfaces[];
1767
1768 if (allow_download->integer && allow_download_maps->integer)
1769 {
1770 while (precache_tex < numtexinfo)
1771 {
1772 char fn[MAX_OSPATH];
1773
1774 sprintf(fn, "textures/%s.tga", map_surfaces[precache_tex++].rname);
1775 if (!CL_CheckOrDownloadFile(fn))
1776 return; // started a download
1777 }
1778 }
1779 precache_check = SCRIPT_CNT;
1780 }
1781
1782 //get map related scripts
1783 if (precache_check == SCRIPT_CNT)
1784 {
1785 precache_check = SCRIPT_CNT+1;
1786 if (allow_download_maps->integer)
1787 {
1788 //get fog files
1789 COM_StripExtension ( cl.configstrings[CS_MODELS+1], fn );
1790
1791 //remove "maps/" from string
1792 for(i = 5, j = 0; i < strlen(fn); i++, j++)
1793 {
1794 map[j] = fn[i];
1795 }
1796 map[i-5] = 0;
1797
1798 Com_sprintf(script, sizeof(script), "maps/scripts/%s.fog", map);
1799 if (!CL_CheckOrDownloadFile(script))
1800 return; // started a download
1801 }
1802 }
1803
1804 if (precache_check == SCRIPT_CNT+1)
1805 {
1806 precache_check = SCRIPT_CNT+2;
1807 if (allow_download_maps->integer)
1808 {
1809 //get mus files
1810 COM_StripExtension ( cl.configstrings[CS_MODELS+1], fn );
1811
1812 //remove "maps/" from string
1813 for(i = 5, j = 0; i < strlen(fn); i++, j++)
1814 map[j] = fn[i];
1815 map[i-5] = 0;
1816
1817 Com_sprintf(script, sizeof(script), "maps/scripts/%s.mus", map);
1818 if (!CL_CheckOrDownloadFile(script))
1819 return; // started a download
1820 }
1821 }
1822
1823 if (precache_check == SCRIPT_CNT+2)
1824 {
1825 precache_check = SCRIPT_CNT+3;
1826 if (allow_download_maps->integer)
1827 {
1828 //get rscript files
1829 COM_StripExtension ( cl.configstrings[CS_MODELS+1], fn );
1830
1831 //remove "maps/" from string
1832 for(i = 5, j = 0; i < strlen(fn); i++, j++)
1833 map[j] = fn[i];
1834 map[i-5] = 0;
1835
1836 Com_sprintf(script, sizeof(script), "scripts/maps/%s.rscript", map);
1837 if (!CL_CheckOrDownloadFile(script))
1838 return; // started a download
1839 }
1840 }
1841
1842 if (precache_check == SCRIPT_CNT+3) // try downloading lightmap
1843 {
1844 precache_check = SCRIPT_CNT+4;
1845 if (allow_download_maps->integer)
1846 {
1847 char *extension;
1848 strncpy (fn, cl.configstrings[CS_MODELS+1], MAX_OSPATH-1-strlen(".lightmap")+strlen(".bsp"));
1849 extension = strstr (fn, ".bsp");
1850 if (extension)
1851 *extension = 0;
1852 strcat (fn, ".lightmap");
1853 if (!CL_CheckOrDownloadFile(fn))
1854 return; // started a download
1855 }
1856 }
1857
1858 //ZOID
1859 CL_RegisterSounds ();
1860 CL_PrepRefresh ();
1861
1862 MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
1863 MSG_WriteString (&cls.netchan.message, va("begin %i\n", precache_spawncount) );
1864
1865 {
1866 netadr_t adr;
1867
1868 if (!NET_StringToAdr (cls.servername, &adr))
1869 {
1870 Com_Printf ("Bad address: %s\n", cls.servername);
1871 return;
1872 }
1873
1874 // default true to avoid messing up legacy ctf servers
1875 // legacy DM servers don't send visibility lights anyway
1876 server_is_team = true;
1877 Netchan_OutOfBandPrint (NS_CLIENT, adr, "teamgame\n");
1878 }
1879 }
1880
1881
CL_GetRemoteServer(void)1882 netadr_t *CL_GetRemoteServer (void) {
1883 static netadr_t remoteserver;
1884 if (cls.state >= ca_connected) {
1885 remoteserver = cls.netchan.remote_address;
1886 return &remoteserver;
1887 }
1888 return NULL;
1889 }
1890
1891
1892 /*
1893 =================
1894 CL_Precache_f
1895
1896 The server will send this command right
1897 before allowing the client into the server
1898 =================
1899 */
CL_Precache_f(void)1900 void CL_Precache_f (void)
1901 {
1902 //Yet another hack to let old demos work
1903 //the old precache sequence
1904 if (Cmd_Argc() < 2)
1905 {
1906 unsigned map_checksum; // for detecting cheater maps
1907
1908 //FIXME: support for new map specification files
1909 CM_LoadBSP (cl.configstrings[CS_MODELS+1], true, &map_checksum);
1910 CL_RegisterSounds ();
1911 CL_PrepRefresh ();
1912 return;
1913 }
1914
1915 precache_check = CS_MODELS;
1916 precache_spawncount = atoi(Cmd_Argv(1));
1917 precache_model = 0;
1918 precache_model_skin = 0;
1919
1920 CL_RequestNextDownload();
1921 }
1922
1923 /*
1924 =================
1925 CL_InitLocal
1926 =================
1927 */
CL_InitLocal(void)1928 void CL_InitLocal (void)
1929 {
1930 cls.state = ca_disconnected;
1931 cls.realtime = Sys_Milliseconds ();
1932
1933 CL_InitInput ();
1934
1935 CL_InitHttpDownload();
1936
1937 CL_IRCSetup( );
1938
1939 adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE );
1940 adr1 = Cvar_Get( "adr1", "", CVAR_ARCHIVE );
1941 adr2 = Cvar_Get( "adr2", "", CVAR_ARCHIVE );
1942 adr3 = Cvar_Get( "adr3", "", CVAR_ARCHIVE );
1943 adr4 = Cvar_Get( "adr4", "", CVAR_ARCHIVE );
1944 adr5 = Cvar_Get( "adr5", "", CVAR_ARCHIVE );
1945 adr6 = Cvar_Get( "adr6", "", CVAR_ARCHIVE );
1946 adr7 = Cvar_Get( "adr7", "", CVAR_ARCHIVE );
1947 adr8 = Cvar_Get( "adr8", "", CVAR_ARCHIVE );
1948
1949 //
1950 // register our variables
1951 //
1952 cl_stereo_separation = Cvar_Get( "cl_stereo_separation", "0.4", CVAR_ARCHIVE | CVARDOC_FLOAT );
1953 cl_stereo = Cvar_Get( "cl_stereo", "0", CVARDOC_BOOL );
1954 background_music = Cvar_Get("background_music", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1955 background_music_vol = Cvar_Get("background_music_vol", "0.5", CVAR_ARCHIVE | CVARDOC_FLOAT);
1956
1957 cl_add_blend = Cvar_Get ("cl_blend", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1958 cl_add_lights = Cvar_Get ("cl_lights", "1", CVARDOC_BOOL);
1959 cl_add_particles = Cvar_Get ("cl_particles", "1", CVARDOC_BOOL);
1960 cl_add_entities = Cvar_Get ("cl_entities", "1", CVARDOC_BOOL);
1961 cl_gun = Cvar_Get ("cl_gun", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1962 cl_footsteps = Cvar_Get ("cl_footsteps", "1", CVARDOC_BOOL);
1963 cl_noskins = Cvar_Get ("cl_noskins", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
1964 cl_autoskins = Cvar_Get ("cl_autoskins", "0", CVARDOC_BOOL);
1965 cl_predict = Cvar_Get ("cl_predict", "1", CVARDOC_BOOL);
1966 cl_maxfps = Cvar_Get ("cl_maxfps", "60", CVAR_ARCHIVE | CVARDOC_INT);
1967 cl_showPlayerNames = Cvar_Get ("cl_showplayernames", "0", CVAR_ARCHIVE | CVARDOC_INT);
1968 Cvar_Describe (cl_showPlayerNames, "0 means no nametags, 1 means show one nametag in the center of the screen, 2 means show a nametag over each player.");
1969 cl_healthaura = Cvar_Get ("cl_healthaura", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1970 Cvar_Describe (cl_healthaura, "show glowing effects around health pickups.");
1971 cl_noblood = Cvar_Get ("cl_noblood", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
1972 cl_disbeamclr = Cvar_Get("cl_disbeamclr", "0", CVAR_ARCHIVE | CVARDOC_INT);
1973 Cvar_Describe (cl_disbeamclr, "beam color for the disruptor weapon. 0 = green, 1 = blue, 2 = red, 3 = yellow, 4 = purple.");
1974 cl_brass = Cvar_Get ("cl_brass", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1975 cl_dmlights = Cvar_Get("cl_dmlights", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1976 cl_playtaunts = Cvar_Get ("cl_playtaunts", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1977 cl_centerprint = Cvar_Get ("cl_centerprint", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1978 cl_precachecustom = Cvar_Get ("cl_precachecustom", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
1979 Cvar_Describe (cl_precachecustom, "precache 3rd-party and custom player skins at the first map load.");
1980 cl_simpleitems = Cvar_Get ("cl_simpleitems", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
1981 Cvar_Describe (cl_simpleitems, "show sprites instead of models for pickup items.");
1982 cl_flicker = Cvar_Get ("cl_flicker", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
1983 Cvar_Describe (cl_flicker, "enable flickering world lighting.");
1984
1985 cl_paindist = Cvar_Get ("cl_paindist", "1", CVAR_ARCHIVE);
1986 cl_explosiondist = Cvar_Get ("cl_explosiondist", "1", CVAR_ARCHIVE);
1987 cl_raindist = Cvar_Get ("cl_raindist", "1", CVAR_ARCHIVE);
1988
1989 cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0);
1990 cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0);
1991 cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0);
1992 cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", 0);
1993 cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", 0);
1994 cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
1995
1996 cl_run = Cvar_Get ("cl_run", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
1997 freelook = Cvar_Get( "freelook", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
1998 lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
1999 lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
2000 m_accel = Cvar_Get ("m_accel", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
2001 sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE | CVARDOC_FLOAT);
2002 menu_sensitivity = Cvar_Get("menu_sensitivity", "3", CVAR_ARCHIVE | CVARDOC_FLOAT);
2003
2004 m_smoothing = Cvar_Get("m_smoothing", "0", CVAR_ARCHIVE | CVARDOC_BOOL);
2005 m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE | CVARDOC_FLOAT);
2006 m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE | CVARDOC_FLOAT);
2007 m_forward = Cvar_Get ("m_forward", "1", 0);
2008 m_side = Cvar_Get ("m_side", "1", 0);
2009
2010 cl_shownet = Cvar_Get ("cl_shownet", "0", 0);
2011 cl_showmiss = Cvar_Get ("cl_showmiss", "0", 0);
2012 cl_showclamp = Cvar_Get ("showclamp", "0", 0);
2013 cl_timeout = Cvar_Get ("cl_timeout", "120", 0);
2014 cl_paused = Cvar_Get ("paused", "0", 0);
2015 cl_timedemo = Cvar_Get ("timedemo", "0", CVARDOC_BOOL);
2016 Cvar_Describe (cl_timedemo, "play back demos (recorded games) in benchmark mode.");
2017 cl_demoquit = Cvar_Get ("demoquit", "0", CVARDOC_BOOL);
2018 Cvar_Describe (cl_demoquit, "quit automatically after a demo has finished.");
2019
2020 cl_speedrecord = Cvar_Get ("cl_speedrecord", "450", CVARDOC_INT);
2021 cl_alltimespeedrecord = Cvar_Get ("cl_alltimespeedrecord", "450", CVAR_ARCHIVE | CVARDOC_INT);
2022
2023 rcon_client_password = Cvar_Get ("rcon_password", "", CVARDOC_STR);
2024 rcon_address = Cvar_Get ("rcon_address", "", CVARDOC_STR);
2025
2026 cl_test = Cvar_Get ("cl_test", "0", CVAR_ARCHIVE);
2027
2028 //
2029 // userinfo
2030 //
2031 info_password = Cvar_Get ("password", "", CVAR_USERINFO | CVARDOC_STR);
2032 info_spectator = Cvar_Get ("spectator", "0", CVAR_USERINFO | CVARDOC_BOOL);
2033 info_spectator_password = Cvar_Get ( "spectator_password", "", CVAR_USERINFO | CVARDOC_STR);
2034 name = Cvar_Get ("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE | CVARDOC_STR);
2035 stats_password = Cvar_Get("stats_password", "password", CVAR_PROFILE | CVARDOC_STR);
2036 Q_strncpyz2(currLoginState.old_password, stats_password->string, sizeof(currLoginState.old_password));
2037 currLoginState.hashed = false;
2038 pw_hashed = Cvar_Get("stats_pw_hashed", "0", CVAR_PROFILE | CVARDOC_BOOL);
2039 /* */
2040 ValidatePlayerName( name->string, (strlen(name->string)+1) );
2041 /* */
2042 skin = Cvar_Get ("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE | CVARDOC_STR);
2043 rate = Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE | CVARDOC_INT); // FIXME
2044 msg = Cvar_Get ("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE);
2045 hand = Cvar_Get ("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
2046 fov = Cvar_Get ("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE | CVARDOC_INT);
2047 Cvar_Describe (fov, "horizontal field of view (in degrees.) Maximum 160.");
2048 gender = Cvar_Get ("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE | CVARDOC_STR);
2049 gender_auto = Cvar_Get ("gender_auto", "1", CVAR_ARCHIVE);
2050 gender->modified = false; // clear this so we know when user sets it manually
2051
2052 cl_vwep = Cvar_Get ("cl_vwep", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
2053 cl_vehicle_huds = Cvar_Get ("cl_vehicle_huds", "1", CVAR_ARCHIVE | CVARDOC_BOOL);
2054
2055 cl_master = Cvar_Get ("cl_master", "master.corservers.com", CVAR_ARCHIVE | CVARDOC_STR);
2056 cl_master2 = Cvar_Get ("cl_master2", "master2.corservers.com", CVAR_ARCHIVE | CVARDOC_STR);
2057
2058 //custom huds
2059 cl_hudimage1 = Cvar_Get("cl_hudimage1", "pics/i_health.tga", CVAR_ARCHIVE | CVARDOC_STR);
2060 cl_hudimage2 = Cvar_Get("cl_hudimage2", "pics/i_score.tga", CVAR_ARCHIVE | CVARDOC_STR);
2061
2062 //stats server
2063 cl_stats_server = Cvar_Get("cl_stats_server", "http://stats.planetarena.org", CVAR_ARCHIVE | CVARDOC_STR);
2064
2065 //update checker
2066 cl_latest_game_version_url = Cvar_Get("cl_latest_game_version_server", "http://red.planetarena.org/version/crx_version", CVAR_ARCHIVE | CVARDOC_STR);
2067
2068 //throwaway cvars
2069 Cvar_Get("g_dm_lights", "1", CVAR_ARCHIVE | CVAR_GAMEINFO | CVARDOC_BOOL); //mark this as archived even if game code doesn't run.
2070
2071 //
2072 // register our commands
2073 //
2074 Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
2075 Cmd_AddCommand ("pause", CL_Pause_f);
2076 Cmd_AddCommand ("pingservers", CL_PingServers_f);
2077 Cmd_AddCommand ("skins", CL_Skins_f);
2078
2079 Cmd_AddCommand ("userinfo", CL_Userinfo_f);
2080 Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
2081
2082 Cmd_AddCommand ("changing", CL_Changing_f);
2083 Cmd_AddCommand ("disconnect", CL_Disconnect_f);
2084 Cmd_AddCommand ("record", CL_Record_f);
2085 Cmd_AddCommand ("stop", CL_Stop_f);
2086
2087 Cmd_AddCommand ("quit", CL_Quit_f);
2088
2089 Cmd_AddCommand ("connect", CL_Connect_f);
2090 Cmd_AddCommand ("reconnect", CL_Reconnect_f);
2091
2092 Cmd_AddCommand ("rcon", CL_Rcon_f);
2093
2094 Cmd_AddCommand ("setenv", CL_Setenv_f );
2095
2096 Cmd_AddCommand ("precache", CL_Precache_f);
2097
2098 Cmd_AddCommand ("download", CL_Download_f);
2099
2100 Cmd_AddCommand ("irc_connect", CL_InitIRC);
2101 Cmd_AddCommand ("irc_quit", CL_IRCInitiateShutdown);
2102 Cmd_AddCommand ("irc_say", CL_IRCSay);
2103
2104 //
2105 // forward to server commands
2106 //
2107 // the only thing this does is allow command completion
2108 // to work -- all unknown commands are automatically
2109 // forwarded to the server. It also prevents the commands
2110 // from being ignored if they are issued in 'forced' mode.
2111 Cmd_AddCommand ("wave", NULL);
2112 Cmd_AddCommand ("inven", NULL);
2113 Cmd_AddCommand ("kill", NULL);
2114 Cmd_AddCommand ("use", NULL);
2115 Cmd_AddCommand ("drop", NULL);
2116 Cmd_AddCommand ("say", NULL);
2117 Cmd_AddCommand ("say_team", NULL);
2118 Cmd_AddCommand ("info", NULL);
2119 Cmd_AddCommand ("prog", NULL);
2120 Cmd_AddCommand ("give", NULL);
2121 Cmd_AddCommand ("god", NULL);
2122 Cmd_AddCommand ("notarget", NULL);
2123 Cmd_AddCommand ("noclip", NULL);
2124 Cmd_AddCommand ("invuse", NULL);
2125 Cmd_AddCommand ("invprevw", NULL);
2126 Cmd_AddCommand ("invnextw", NULL);
2127 Cmd_AddCommand ("invprevp", NULL);
2128 Cmd_AddCommand ("invnextp", NULL);
2129 Cmd_AddCommand ("invprev", NULL);
2130 Cmd_AddCommand ("invnext", NULL);
2131 Cmd_AddCommand ("invdrop", NULL);
2132 Cmd_AddCommand ("weapnext", NULL);
2133 Cmd_AddCommand ("weapprev", NULL);
2134 Cmd_AddCommand ("weaplast", NULL);
2135 Cmd_AddCommand ("chatbubble", NULL);
2136 Cmd_AddCommand ("players", NULL);
2137 Cmd_AddCommand ("score", NULL);
2138 Cmd_AddCommand ("vote", NULL);
2139 Cmd_AddCommand ("putaway", NULL);
2140 Cmd_AddCommand ("playerlist", NULL);
2141
2142 Cvar_SetValue("scriptsloaded", 0);
2143
2144 strcpy(map_music, "music/none.wav");
2145
2146 //register all our menu gfx
2147 (void)R_RegisterPic("m_main");
2148 (void)R_RegisterPic("m_options");
2149 (void)R_RegisterPic("menu_back");
2150 (void)R_RegisterPic("m_video");
2151 (void)R_RegisterPic("m_controls");
2152 (void)R_RegisterPic("m_player");
2153 (void)R_RegisterPic("m_bots");
2154 (void)R_RegisterPic("m_startserver");
2155 (void)R_RegisterPic("m_dmoptions");
2156 (void)R_RegisterPic("m_mutators");
2157 (void)R_RegisterPic("m_single");
2158 (void)R_RegisterPic("m_quit");
2159 (void)R_RegisterPic("m_main_mont0");
2160 (void)R_RegisterPic("m_main_mont1");
2161 (void)R_RegisterPic("m_main_mont2");
2162 (void)R_RegisterPic("m_main_mont3");
2163 (void)R_RegisterPic("m_main_mont4");
2164 (void)R_RegisterPic("m_main_mont5");
2165 (void)R_RegisterPic("hud_bomber");
2166 (void)R_RegisterPic("hud_strafer");
2167 (void)R_RegisterPic("hud_hover");
2168 (void)R_RegisterPic("blood_ring");
2169
2170 remoteserver_runspeed = 300; //default
2171 }
2172
2173
2174
2175 /*
2176 ===============
2177 CL_WriteConfiguration
2178
2179 Writes key bindings and archived cvars to config.cfg
2180 ===============
2181 */
CL_WriteConfiguration(void)2182 void CL_WriteConfiguration (void)
2183 {
2184 FILE *f;
2185 char path[MAX_OSPATH];
2186
2187 if (cls.state == ca_uninitialized)
2188 return;
2189
2190 FS_FullWritePath( path, sizeof(path), "config.cfg");
2191 f = fopen (path, "w");
2192 if (!f)
2193 {
2194 Com_Printf ("Couldn't write config.cfg.\n");
2195 return;
2196 }
2197
2198 fprintf (f, "// generated by Alien Arena. Use autoexec.cfg for custom settings.\n");
2199 Key_WriteBindings (f);
2200 fclose (f);
2201
2202 Cvar_WriteVariables (path);
2203 }
2204
2205 /*
2206 ===============
2207 CL_WriteProfile
2208
2209 Writes profile information to profile.cfg
2210 ===============
2211 */
CL_WriteProfile(void)2212 void CL_WriteProfile (void)
2213 {
2214 FILE *f;
2215 char path[MAX_OSPATH];
2216
2217 if (cls.state == ca_uninitialized)
2218 return;
2219
2220 if(!currLoginState.hashed)
2221 return; //We don't ever want to write out non-hashed passwords, period!
2222
2223 FS_FullWritePath( path, sizeof(path), "profile.cfg");
2224 f = fopen (path, "w");
2225 if (!f)
2226 {
2227 Com_Printf ("Couldn't write profile.cfg.\n");
2228 return;
2229 }
2230
2231 fprintf (f, "// generated by Alien Arena.\n");
2232 fprintf (f, "set stats_password %s\n", stats_password->string);
2233 fprintf (f, "set stats_pw_hashed %i\n", pw_hashed->integer);
2234 fclose (f);
2235 }
2236
2237
2238
2239 /*
2240 ==================
2241 CL_FixCvarCheats
2242
2243 ==================
2244 */
2245
2246 typedef struct
2247 {
2248 char *name;
2249 char *value;
2250 cvar_t *var;
2251 } cheatvar_t;
2252
2253 cheatvar_t cheatvars[] =
2254 {
2255 {"timescale", "1", NULL},
2256 {"timedemo", "0", NULL},
2257 {"r_drawworld", "1", NULL},
2258 {"cl_testlights", "0", NULL},
2259 {"r_fullbright", "0", NULL},
2260 {"r_drawflat", "0", NULL},
2261 {"paused", "0", NULL},
2262 {"fixedtime", "0", NULL},
2263 {"gl_lightmap", "0", NULL},
2264 {"gl_showpolys", "0", NULL},
2265 {NULL, NULL, NULL}
2266 };
2267
CL_FixCvarCheats(void)2268 void CL_FixCvarCheats (void)
2269 {
2270 static int numcheatvars = 0;
2271 int i;
2272 cheatvar_t *var;
2273
2274 if ( !strcmp(cl.configstrings[CS_MAXCLIENTS], "1")
2275 || !cl.configstrings[CS_MAXCLIENTS][0]
2276 || cl.attractloop )
2277 return; // single player or demo playback can cheat
2278
2279 // find all the cvars if we haven't done it yet
2280 if (!numcheatvars)
2281 {
2282 while (cheatvars[numcheatvars].name)
2283 {
2284 cheatvars[numcheatvars].var = Cvar_Get (cheatvars[numcheatvars].name,
2285 cheatvars[numcheatvars].value, 0);
2286 numcheatvars++;
2287 }
2288 }
2289
2290 // make sure they are all set to the proper values
2291 for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
2292 {
2293 if ( cl.attractloop &&
2294 (!strcmp( var->name, "timedemo" )
2295 ||
2296 !strcmp( var->name, "timescale"))
2297 )
2298 continue; // allow these when running a .dm2 demo
2299
2300 if ( strcmp (var->var->string, var->value) )
2301 {
2302 Cvar_Set (var->name, var->value);
2303 }
2304 }
2305 }
2306
CL_CheckFlagStatus(void)2307 void CL_CheckFlagStatus( void )
2308 {
2309 if(r_gotFlag)
2310 {
2311 //start the new music
2312 S_StartMusic(map_music_sec);
2313 }
2314 else if(r_lostFlag)
2315 {
2316 //start the original map music back up
2317 S_StartMusic(map_music);
2318 }
2319 }
2320
2321 //============================================================================
2322
2323 qboolean send_packet_now = false; // instant packets. used during downloads
2324 extern float r_frametime; // TODO: move to appropriate .h
2325 extern unsigned sys_frame_time; // TODO: ditto
2326
2327 /**
2328 * @brief Top level client-side routine for main loop.
2329 *
2330 * @param msec the time since the previous CL_Frame.
2331 */
2332 /*
2333 * Notes on time variables:
2334 * cls.frametime :
2335 * float seconds since last render
2336 * clamped for packet processing, now unclamped for rendering
2337 * used in client-to-server move message
2338 * in CL_AddClEntities(): used in bouncing entities (brass) calculations
2339 *
2340 * r_frametime :
2341 * unclamped float seconds since last render. Used in particle rendering.
2342 *
2343 * cl.time :
2344 * critical global timer used in various places.
2345 * clamped to [cl.frame.servertime-100, cl.frame.servertime] in CL_ParseFrame()
2346 *
2347 * cls.realtime :
2348 * set to time at start of frame. Updated anywhere else?
2349 *
2350 * cl.frame.servertime:
2351 * equivalent to server frame number times 100msecs. see CL_ParseFrame()
2352 */
2353
2354 /* Packet Rate Limiting Cap in milliseconds
2355 * msecs=PPS :: 12=83, 13=76, 14=71, 15=66, 16=62
2356 * Current choice is 16msec/62PPS nominal.
2357 * This matches the default cl_maxfps setting.
2358 * Which is 60, of course, but because of msec rounding, the result is 62
2359 * This results in 6 packets per server frame, mostly.
2360 * Packet rate limiting is not invoked unless the PPS is set higher than the FPS.
2361 * Seems like a good idea not to invoke packet rate limiting unless the
2362 * cl_maxfps is set higher than the default.
2363 *
2364 * PKTRATE_EARLY is the minimum msecs for catching up when packets are delayed
2365 */
2366 #define PKTRATE_CAP 16
2367 #define PKTRATE_EARLY 12
2368
CL_Frame(int msec)2369 void CL_Frame( int msec )
2370 {
2371 // static int lasttimecalled = 0; // TODO: see below, obsolete logging?
2372
2373 static int frcjitter[4] = { 0, -1, 0, 1 };
2374 static int frcjitter_ix = 0;
2375 static int framerate_cap = 0;
2376 static int packetrate_cap = 0;
2377 static int packet_timer = 0;
2378 static int render_timer = 0;
2379 static int render_counter = 0;
2380 static int packet_delay = 0;
2381 int render_trigger;
2382 int packet_trigger;
2383 static perftest_t *speedometer = NULL;
2384 static perftest_t *accelerometer = NULL;
2385
2386 if ( dedicated->integer )
2387 { // crx running as dedicated server crashes without this.
2388 return;
2389 }
2390
2391 cls.realtime = curtime; // time at start of Qcommon_Frame()
2392 cls.frametime = 0.0f; // zero here for debug purposes, set below
2393 cl.time += msec; // clamped to [cl.frame.servertime-100,cl.frame.servertime]
2394
2395 /* local timers for decoupling frame rate from packet rate */
2396 packet_timer += msec;
2397 render_timer += msec;
2398
2399 if (!speedometer) {
2400 speedometer = get_perftest("speedometer");
2401 if (speedometer) {
2402 speedometer->is_timerate = false;
2403 speedometer->cvar = Cvar_Get("cl_showspeedometer", "0", CVAR_ARCHIVE);
2404 strcpy (speedometer->format, "speed %4.0f u/s");
2405 speedometer->scale = 1.0f;///12.3f;
2406 }
2407 }
2408
2409 if (!accelerometer) {
2410 accelerometer = get_perftest("acceleromter");
2411 if (accelerometer) {
2412 accelerometer->is_timerate = true;
2413 accelerometer->cvar = Cvar_Get("cl_showaccelerometer", "0", CVAR_ARCHIVE);
2414 strcpy (accelerometer->format, "accel %4.0f u/s/s");
2415 accelerometer->scale = 1.0f;
2416 }
2417 }
2418
2419
2420 /*
2421 * update maximum FPS.
2422 * framerate_cap is in msecs/frame and is user specified.
2423 * Note: Quake2World sets a hard lower limit of 30. Not sure if
2424 * that is needed; to be determined if that is a good thing to do.
2425 */
2426 if ( cl_maxfps->modified )
2427 {
2428 if ( cl_maxfps->value < 30.0f )
2429 {
2430 Com_Printf("Warning: cl_maxfps set to less than 30.\n");
2431 if ( cl_maxfps->value < 1.0f )
2432 {
2433 Cvar_ForceSet( "cl_maxfps", "1" );
2434 }
2435 }
2436 cl_maxfps->modified = false;
2437 framerate_cap = 0; // force a recalculation
2438 }
2439 if ( framerate_cap < 1 )
2440 {
2441 framerate_cap = (int)(ceil( 1000.0f / cl_maxfps->value ));
2442 if ( framerate_cap < 1 )
2443 {
2444 framerate_cap = 1;
2445 }
2446 Com_DPrintf("framerate_cap set to %i msec\n", framerate_cap );
2447 }
2448
2449 /*
2450 * Set nominal milliseconds-per-packet for client-to-server messages.
2451 * Idea is to be timely in getting and transmitting player input without
2452 * congesting the server.
2453 * Plan is to not implement a user setting for this unless a need for that
2454 * is discovered.
2455 * If the cl_maxfps is set low, then FPS will limit PPS, and
2456 * packet rate limiting is bypassed.
2457 */
2458 if ( cls.state == ca_connected )
2459 { // receiving configstrings from the server, run at nominal 10PPS
2460 // avoids unnecessary load on the server
2461 if ( packetrate_cap != 100 )
2462 Com_DPrintf("packet rate change: 10 PPS\n");
2463 packetrate_cap = 100;
2464 }
2465 else if ( framerate_cap >= PKTRATE_CAP )
2466 { // do not to throttle packet sending, run in sync with render
2467 if ( packetrate_cap != -1)
2468 Com_DPrintf("packetrate change: framerate\n");
2469 packetrate_cap = -1;
2470 }
2471 else
2472 { // packet rate limiting
2473 if ( packetrate_cap != PKTRATE_CAP )
2474 Com_DPrintf("packetrate change: %iPPS\n", 1000/PKTRATE_CAP);
2475 packetrate_cap = PKTRATE_CAP;
2476 }
2477
2478 /* local triggers for decoupling framerate from packet rate */
2479 render_trigger = 0;
2480 packet_trigger = 0;
2481
2482 if ( cl_timedemo->integer == 1 )
2483 { /* accumulate timed demo statistics, free run both packet and render */
2484 /* setting render_trigger to 1 forces timedemo_start to be set if it
2485 * hasn't been already. It also forces cl.timedemo_frames to be
2486 * incremented.
2487 */
2488 render_trigger = 1;
2489 packet_trigger = 1;
2490 }
2491 else
2492 { /* normal operation. */
2493 /* Sometimes, the packetrate_cap can be "in phase" with
2494 * the frame rate affecting the average packets-per-server-frame.
2495 * A little jitter in the framerate_cap counteracts that.
2496 */
2497 if ( render_timer >= (framerate_cap + frcjitter[frcjitter_ix]) )
2498 {
2499 if ( ++frcjitter_ix > 3 ) frcjitter_ix = 0;
2500 render_trigger = 1;
2501 }
2502 if ( packetrate_cap == -1 )
2503 { // flagged to run same as framerate_cap
2504 packet_trigger = render_trigger;
2505 }
2506 else if ( packet_timer >= packetrate_cap )
2507 { // normal packet trigger
2508 packet_trigger = 1;
2509 }
2510 else if ( packet_delay > 0 )
2511 { // packet sent in previous loop was delayed
2512 if ( packet_timer >= PKTRATE_EARLY )
2513 { // should be ok to send a packet early
2514 /* idea is to try to maintain a steady number of
2515 * client-to-server packets per server frame.
2516 * If render is triggered, it is good to poll input and
2517 * send a packet to avoid more delay.
2518 * If adding the delay to the timer reaches the cap then
2519 * try to catch up
2520 * Otherwise, do nothing, the next loop should occur soon.
2521 */
2522 if ( render_trigger
2523 || ((packet_timer + packet_delay) >= packetrate_cap) )
2524 {
2525 packet_trigger = 1;
2526 }
2527 }
2528 }
2529 }
2530
2531 if ( packet_trigger || send_packet_now || cls.download)
2532 {
2533 send_packet_now = false; // used during downloads
2534
2535 if ( packetrate_cap > 0 && packet_timer > packetrate_cap )
2536 { // difference between cap and timer, a delayed packet
2537 packet_delay = packet_timer - packetrate_cap;
2538 }
2539 else
2540 {
2541 packet_delay = 0;
2542 }
2543
2544 render_counter = 0; // for counting renders since last packet
2545
2546 /* let the mouse activate or deactivate */
2547 IN_Frame();
2548
2549 /*
2550 * calculate frametime in seconds for packet procedures
2551 * cls.frametime is source for the cmd.msecs byte
2552 * in the client-to-server move message.
2553 */
2554 cls.frametime = ((float)packet_timer) / 1000.0f;
2555 if ( cls.frametime >= 0.250f )
2556 { /* very long delay */
2557 /*
2558 * server checks for cmd.msecs to be <= 250
2559 */
2560 Com_DPrintf("CL_Frame(): cls.frametime clamped from %0.8f to 0.24999\n",
2561 cls.frametime );
2562 cls.frametime = 0.24999f ;
2563 /*
2564 * try to throttle the video frame rate by overriding the
2565 * render trigger.
2566 */
2567 render_trigger = false;
2568 render_timer = 0;
2569 }
2570
2571 /* process server-to-client packets */
2572 CL_ReadPackets();
2573
2574 /* execute pending commands */
2575 Cbuf_Execute();
2576
2577 /* run cURL downloads */
2578 CL_HttpDownloadThink();
2579
2580 /*
2581 * system dependent keyboard and mouse input event polling
2582 * accumulate keyboard and mouse events
2583 */
2584 Sys_SendKeyEvents();
2585
2586 /* joystick input. may or may not be working. */
2587 IN_Commands();
2588
2589 /* execute pending commands */
2590 Cbuf_Execute();
2591
2592 /*
2593 * send client commands to server
2594 * these are construced from accumulated keyboard and mouse events,
2595 * which are then reset
2596 */
2597 CL_SendCmd();
2598
2599 /* clear various cvars unless single player */
2600 CL_FixCvarCheats ();
2601
2602 /* resend a connection request if necessary */
2603 CL_CheckForResend();
2604
2605 /*
2606 * predict movement for un-acked client-to-server packets
2607 * [The Quake trick that keeps players view smooth in on-line play.]
2608 */
2609 CL_PredictMovement();
2610
2611 if (speedometer && speedometer->cvar->integer) {
2612 speedometer->counter = sqrt(
2613 cl.predicted_velocity[0]*cl.predicted_velocity[0]+
2614 cl.predicted_velocity[1]*cl.predicted_velocity[1]
2615 );
2616 if (speedometer->counter > cl_speedrecord->value) {
2617 Cvar_SetValue ("cl_speedrecord", speedometer->counter);
2618 if (speedometer->counter > cl_alltimespeedrecord->value)
2619 Cvar_SetValue ("cl_alltimespeedrecord", speedometer->counter);
2620 }
2621 }
2622
2623 if (accelerometer && accelerometer->cvar->integer) {
2624 static float old_vel;
2625 float new_vel;
2626 new_vel = sqrt(
2627 cl.predicted_velocity[0]*cl.predicted_velocity[0]+
2628 cl.predicted_velocity[1]*cl.predicted_velocity[1]
2629 );
2630 accelerometer->counter += new_vel-old_vel;
2631 old_vel = new_vel;
2632 }
2633
2634 /* retrigger packet send timer */
2635 packet_timer = 0;
2636 }
2637
2638 /*
2639 * refresh can occur on different frames than client-to-server messages.
2640 * when packet rate limiting is in effect
2641 */
2642 if ( render_trigger )
2643 {
2644 ++render_counter; // counting renders since last packet
2645
2646 if (!packet_trigger && cl_test->integer) //return cl_test - this was causing major issues with menu mouse in windows build
2647 {
2648 /*
2649 * system dependent keyboard and mouse input event polling
2650 * accumulate keyboard and mouse events
2651 */
2652 cls.frametime = ((float)packet_timer) / 1000.0f;
2653 Sys_SendKeyEvents();
2654 /*
2655 * update view angles based on accumulated keyboard and mouse
2656 * events, which are *not* reset
2657 */
2658 IN_Move(NULL);
2659 }
2660
2661 /*
2662 * calculate cls.frametime in seconds for render procedures.
2663 *
2664 * May not need to clamp for rendering.
2665 * Only would affect things if framerate went below 4 FPS.
2666 *
2667 * Using a simple lowpass filter, to smooth out irregular timing.
2668 */
2669 cls.frametime = (float)(render_timer) / 1000.0f;
2670 r_frametime = (r_frametime + cls.frametime + cls.frametime) / 3.0f;
2671 cls.frametime = r_frametime;
2672
2673 // Update the display
2674 VID_CheckChanges();
2675 if ( !cl.refresh_prepped && cls.state == ca_active )
2676 { // re-initialize video configuration
2677 CL_PrepRefresh();
2678 }
2679 else
2680 { // regular screen update
2681 if ( host_speeds->integer )
2682 time_before_ref = Sys_Milliseconds(); // TODO: obsolete test?
2683 SCR_UpdateScreen();
2684 if ( host_speeds->integer )
2685 time_after_ref = Sys_Milliseconds();
2686 }
2687
2688 // check for flag and update music src if possesed or lost
2689 CL_CheckFlagStatus();
2690
2691 // update audio.
2692 S_Update( cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up );
2693
2694 // advance local effects for next frame
2695 CL_RunDLights();
2696 CL_RunLightStyles();
2697 SCR_RunConsole ();
2698
2699 ++cls.framecount; // does not appear to be used anywhere
2700
2701 /* developer test for observing timers */
2702 // Com_DPrintf("rt: %i cft: %f\n", render_timer, cls.frametime );
2703
2704 // retrigger render timing
2705 render_timer = 0;
2706
2707 #if 0
2708 /* TODO: Check if this still works and/or is useful.
2709 */
2710 if ( log_stats->value )
2711 {
2712 if ( cls.state == ca_active )
2713 {
2714 if ( !lasttimecalled )
2715 {
2716 lasttimecalled = Sys_Milliseconds();
2717 if ( log_stats_file )
2718 fprintf( log_stats_file, "0\n" );
2719 }
2720 else
2721 {
2722 int now = Sys_Milliseconds();
2723
2724 if ( log_stats_file )
2725 fprintf( log_stats_file, "%d\n", now - lasttimecalled );
2726 lasttimecalled = now;
2727 }
2728 }
2729 }
2730 #endif
2731
2732 }
2733
2734 }
2735
2736 //============================================================================
2737
2738 /*
2739 ====================
2740 CL_Init
2741 ====================
2742 */
CL_Init(void)2743 void CL_Init (void)
2744 {
2745 if (dedicated->integer)
2746 return; // nothing running on the client
2747
2748 // Initialise fonts
2749 CL_gameFont = &_CL_gameFont;
2750 FNT_AutoInit( CL_gameFont , "freesans" , 0 , 65 , 8 , 48 );
2751 CL_gameFont->faceVar = Cvar_Get( "fnt_game" , "orbitron" , CVAR_ARCHIVE );
2752 CL_gameFont->sizeVar = Cvar_Get( "fnt_game_size" , "0" , CVAR_ARCHIVE );
2753 FNT_AutoRegister( CL_gameFont );
2754
2755 CL_centerFont = &_CL_centerFont;
2756 FNT_AutoInit( CL_centerFont , "freesans" , 0 , 45 , 16 , 64 );
2757 CL_centerFont->faceVar = CL_gameFont->faceVar;
2758 CL_centerFont->sizeVar = Cvar_Get( "fnt_center_size" , "0" , CVAR_ARCHIVE );
2759 FNT_AutoRegister( CL_centerFont );
2760
2761 CL_consoleFont = &_CL_consoleFont;
2762 FNT_AutoInit( CL_consoleFont , "freesans" , 0 , 52 , 8 , 48 );
2763 CL_consoleFont->faceVar = Cvar_Get( "fnt_console" , "freemono" , CVAR_ARCHIVE );
2764 CL_consoleFont->sizeVar = Cvar_Get( "fnt_console_size" , "0" , CVAR_ARCHIVE );
2765 FNT_AutoRegister( CL_consoleFont );
2766
2767 CL_menuFont = &_CL_menuFont;
2768 FNT_AutoInit( CL_menuFont , "freesans" , 0 , 48 , 8 , 48 );
2769 CL_menuFont->faceVar = Cvar_Get( "fnt_menu" , "freesans" , CVAR_ARCHIVE );
2770 CL_menuFont->sizeVar = Cvar_Get( "fnt_menu_size" , "0" , CVAR_ARCHIVE );
2771 FNT_AutoRegister( CL_menuFont );
2772
2773 // all archived variables will now be loaded
2774
2775 CON_Initialise( );
2776
2777 VID_Init ();
2778 S_Init ();
2779
2780 V_Init ();
2781
2782 net_message.data = net_message_buffer;
2783 net_message.maxsize = sizeof(net_message_buffer);
2784
2785 M_Init ();
2786
2787 SCR_Init ();
2788 cls.disable_screen = true; // don't draw yet
2789
2790 CL_InitLocal ();
2791 IN_Init ();
2792
2793 FS_ExecAutoexec (); // add commands from autoexec.cfg
2794 Cbuf_Execute ();
2795
2796 if ( name && name->string[0] )
2797 {
2798 ValidatePlayerName( name->string, (strlen(name->string)+1) );
2799 }
2800 }
2801
2802
2803 /*
2804 ===============
2805 CL_Shutdown
2806
2807 FIXME: this is a callback from Sys_Quit and Com_Error. It would be better
2808 to run quit through here before the final handoff to the sys code.
2809 ===============
2810 */
CL_Shutdown(void)2811 void CL_Shutdown(void)
2812 {
2813 static qboolean isdown = false;
2814
2815 if (isdown)
2816 {
2817 printf ("recursive shutdown\n");
2818 return;
2819 }
2820 isdown = true;
2821
2822 STATS_Logout();
2823
2824 CL_IRCInitiateShutdown();
2825 CL_ShutdownHttpDownload();
2826 CL_WriteConfiguration ();
2827 CL_WriteProfile();
2828
2829 S_Shutdown();
2830 IN_Shutdown ();
2831 VID_Shutdown();
2832 CL_IRCWaitShutdown( );
2833
2834 NET_Shutdown();
2835
2836 RS_FreeAllScripts();
2837 }
2838
2839
2840