File: client\cl_main.c
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 // cl_main.c -- client main loop
21
22 #include "client.h"
23
24 cvar_t *freelook;
25
26 cvar_t *adr0;
27 cvar_t *adr1;
28 cvar_t *adr2;
29 cvar_t *adr3;
30 cvar_t *adr4;
31 cvar_t *adr5;
32 cvar_t *adr6;
33 cvar_t *adr7;
34 cvar_t *adr8;
35
36 cvar_t *cl_stereo_separation;
37 cvar_t *cl_stereo;
38
39 cvar_t *rcon_client_password;
40 cvar_t *rcon_address;
41
42 cvar_t *cl_noskins;
43 cvar_t *cl_autoskins;
44 cvar_t *cl_footsteps;
45 cvar_t *cl_timeout;
46 cvar_t *cl_predict;
47 //cvar_t *cl_minfps;
48 cvar_t *cl_maxfps;
49 cvar_t *cl_gun;
50
51 cvar_t *cl_add_particles;
52 cvar_t *cl_add_lights;
53 cvar_t *cl_add_entities;
54 cvar_t *cl_add_blend;
55
56 cvar_t *cl_shownet;
57 cvar_t *cl_showmiss;
58 cvar_t *cl_showclamp;
59
60 cvar_t *cl_paused;
61 cvar_t *cl_timedemo;
62
63 cvar_t *lookspring;
64 cvar_t *lookstrafe;
65 cvar_t *sensitivity;
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 *cl_lightlevel;
73
74 //
75 // userinfo
76 //
77 cvar_t *info_password;
78 cvar_t *info_spectator;
79 cvar_t *name;
80 cvar_t *skin;
81 cvar_t *rate;
82 cvar_t *fov;
83 cvar_t *msg;
84 cvar_t *hand;
85 cvar_t *gender;
86 cvar_t *gender_auto;
87
88 cvar_t *cl_vwep;
89
90 client_static_t cls;
91 client_state_t cl;
92
93 centity_t cl_entities[MAX_EDICTS];
94
95 entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
96
97 extern cvar_t *allow_download;
98 extern cvar_t *allow_download_players;
99 extern cvar_t *allow_download_models;
100 extern cvar_t *allow_download_sounds;
101 extern cvar_t *allow_download_maps;
102
103 //======================================================================
104
105
106 /*
107 ====================
108 CL_WriteDemoMessage
109
110 Dumps the current net message, prefixed by the length
111 ====================
112 */
113 void CL_WriteDemoMessage (void)
114 {
115 int len, swlen;
116
117 // the first eight bytes are just packet sequencing stuff
118 len = net_message.cursize-8;
119 swlen = LittleLong(len);
120 fwrite (&swlen, 4, 1, cls.demofile);
121 fwrite (net_message.data+8, len, 1, cls.demofile);
122 }
123
124
125 /*
126 ====================
127 CL_Stop_f
128
129 stop recording a demo
130 ====================
131 */
132 void CL_Stop_f (void)
133 {
134 int len;
135
136 if (!cls.demorecording)
137 {
138 Com_Printf ("Not recording a demo.\n");
139 return;
140 }
141
142 // finish up
143 len = -1;
144 fwrite (&len, 4, 1, cls.demofile);
145 fclose (cls.demofile);
146 cls.demofile = NULL;
147 cls.demorecording = false;
148 Com_Printf ("Stopped demo.\n");
149 }
150
151 /*
152 ====================
153 CL_Record_f
154
155 record <demoname>
156
157 Begins recording a demo from the current position
158 ====================
159 */
160 void CL_Record_f (void)
161 {
162 char name[MAX_OSPATH];
163 char buf_data[MAX_MSGLEN];
164 sizebuf_t buf;
165 int i;
166 int len;
167 entity_state_t *ent;
168 entity_state_t nullstate;
169
170 if (Cmd_Argc() != 2)
171 {
172 Com_Printf ("record <demoname>\n");
173 return;
174 }
175
176 if (cls.demorecording)
177 {
178 Com_Printf ("Already recording.\n");
179 return;
180 }
181
182 if (cls.state != ca_active)
183 {
184 Com_Printf ("You must be in a level to record.\n");
185 return;
186 }
187
188 //
189 // open the demo file
190 //
191 Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
192
193 Com_Printf ("recording to %s.\n", name);
194 FS_CreatePath (name);
195 cls.demofile = fopen (name, "wb");
196 if (!cls.demofile)
197 {
198 Com_Printf ("ERROR: couldn't open.\n");
199 return;
200 }
201 cls.demorecording = true;
202
203 // don't start saving messages until a non-delta compressed message is received
204 cls.demowaiting = true;
205
206 //
207 // write out messages to hold the startup information
208 //
209 SZ_Init (&buf, buf_data, sizeof(buf_data));
210
211 // send the serverdata
212 MSG_WriteByte (&buf, svc_serverdata);
213 MSG_WriteLong (&buf, PROTOCOL_VERSION);
214 MSG_WriteLong (&buf, 0x10000 + cl.servercount);
215 MSG_WriteByte (&buf, 1); // demos are always attract loops
216 MSG_WriteString (&buf, cl.gamedir);
217 MSG_WriteShort (&buf, cl.playernum);
218
219 MSG_WriteString (&buf, cl.configstrings[CS_NAME]);
220
221 // configstrings
222 for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
223 {
224 if (cl.configstrings[i][0])
225 {
226 if (buf.cursize + strlen (cl.configstrings[i]) + 32 > buf.maxsize)
227 { // write it out
228 len = LittleLong (buf.cursize);
229 fwrite (&len, 4, 1, cls.demofile);
230 fwrite (buf.data, buf.cursize, 1, cls.demofile);
231 buf.cursize = 0;
232 }
233
234 MSG_WriteByte (&buf, svc_configstring);
235 MSG_WriteShort (&buf, i);
236 MSG_WriteString (&buf, cl.configstrings[i]);
237 }
238
239 }
240
241 // baselines
242 memset (&nullstate, 0, sizeof(nullstate));
243 for (i=0; i<MAX_EDICTS ; i++)
244 {
245 ent = &cl_entities[i].baseline;
246 if (!ent->modelindex)
247 continue;
248
249 if (buf.cursize + 64 > buf.maxsize)
250 { // write it out
251 len = LittleLong (buf.cursize);
252 fwrite (&len, 4, 1, cls.demofile);
253 fwrite (buf.data, buf.cursize, 1, cls.demofile);
254 buf.cursize = 0;
255 }
256
257 MSG_WriteByte (&buf, svc_spawnbaseline);
258 MSG_WriteDeltaEntity (&nullstate, &cl_entities[i].baseline, &buf, true, true);
259 }
260
261 MSG_WriteByte (&buf, svc_stufftext);
262 MSG_WriteString (&buf, "precache\n");
263
264 // write it to the demo file
265
266 len = LittleLong (buf.cursize);
267 fwrite (&len, 4, 1, cls.demofile);
268 fwrite (buf.data, buf.cursize, 1, cls.demofile);
269
270 // the rest of the demo file will be individual frames
271 }
272
273 //======================================================================
274
275 /*
276 ===================
277 Cmd_ForwardToServer
278
279 adds the current command line as a clc_stringcmd to the client message.
280 things like godmode, noclip, etc, are commands directed to the server,
281 so when they are typed in at the console, they will need to be forwarded.
282 ===================
283 */
284 void Cmd_ForwardToServer (void)
285 {
286 char *cmd;
287
288 cmd = Cmd_Argv(0);
289 if (cls.state <= ca_connected || *cmd == '-' || *cmd == '+')
290 {
291 Com_Printf ("Unknown command \"%s\"\n", cmd);
292 return;
293 }
294
295 MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
296 SZ_Print (&cls.netchan.message, cmd);
297 if (Cmd_Argc() > 1)
298 {
299 SZ_Print (&cls.netchan.message, " ");
300 SZ_Print (&cls.netchan.message, Cmd_Args());
301 }
302 }
303
304 void CL_Setenv_f( void )
305 {
306 int argc = Cmd_Argc();
307
308 if ( argc > 2 )
309 {
310 char buffer[1000];
311 int i;
312
313 strcpy( buffer, Cmd_Argv(1) );
314 strcat( buffer, "=" );
315
316 for ( i = 2; i < argc; i++ )
317 {
318 strcat( buffer, Cmd_Argv( i ) );
319 strcat( buffer, " " );
320 }
321
322 putenv( buffer );
323 }
324 else if ( argc == 2 )
325 {
326 char *env = getenv( Cmd_Argv(1) );
327
328 if ( env )
329 {
330 Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
331 }
332 else
333 {
334 Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
335 }
336 }
337 }
338
339
340 /*
341 ==================
342 CL_ForwardToServer_f
343 ==================
344 */
345 void CL_ForwardToServer_f (void)
346 {
347 if (cls.state != ca_connected && cls.state != ca_active)
348 {
349 Com_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
350 return;
351 }
352
353 // don't forward the first argument
354 if (Cmd_Argc() > 1)
355 {
356 MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
357 SZ_Print (&cls.netchan.message, Cmd_Args());
358 }
359 }
360
361
362 /*
363 ==================
364 CL_Pause_f
365 ==================
366 */
367 void CL_Pause_f (void)
368 {
369 // never pause in multiplayer
370 if (Cvar_VariableValue ("maxclients") > 1 || !Com_ServerState ())
371 {
372 Cvar_SetValue ("paused", 0);
373 return;
374 }
375
376 Cvar_SetValue ("paused", !cl_paused->value);
377 }
378
379 /*
380 ==================
381 CL_Quit_f
382 ==================
383 */
384 void CL_Quit_f (void)
385 {
386 CL_Disconnect ();
387 Com_Quit ();
388 }
389
390 /*
391 ================
392 CL_Drop
393
394 Called after an ERR_DROP was thrown
395 ================
396 */
397 void CL_Drop (void)
398 {
399 if (cls.state == ca_uninitialized)
400 return;
401 if (cls.state == ca_disconnected)
402 return;
403
404 CL_Disconnect ();
405
406 // drop loading plaque unless this is the initial game start
407 if (cls.disable_servercount != -1)
408 SCR_EndLoadingPlaque (); // get rid of loading plaque
409 }
410
411
412 /*
413 =======================
414 CL_SendConnectPacket
415
416 We have gotten a challenge from the server, so try and
417 connect.
418 ======================
419 */
420 void CL_SendConnectPacket (void)
421 {
422 netadr_t adr;
423 int port;
424
425 if (!NET_StringToAdr (cls.servername, &adr))
426 {
427 Com_Printf ("Bad server address\n");
428 cls.connect_time = 0;
429 return;
430 }
431 if (adr.port == 0)
432 adr.port = BigShort (PORT_SERVER);
433
434 port = Cvar_VariableValue ("qport");
435 userinfo_modified = false;
436
437 Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n",
438 PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() );
439 }
440
441 /*
442 =================
443 CL_CheckForResend
444
445 Resend a connect message if the last one has timed out
446 =================
447 */
448 void CL_CheckForResend (void)
449 {
450 netadr_t adr;
451
452 // if the local server is running and we aren't
453 // then connect
454 if (cls.state == ca_disconnected && Com_ServerState() )
455 {
456 cls.state = ca_connecting;
457 strncpy (cls.servername, "localhost", sizeof(cls.servername)-1);
458 // we don't need a challenge on the localhost
459 CL_SendConnectPacket ();
460 return;
461 // cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
462 }
463
464 // resend if we haven't gotten a reply yet
465 if (cls.state != ca_connecting)
466 return;
467
468 if (cls.realtime - cls.connect_time < 3000)
469 return;
470
471 if (!NET_StringToAdr (cls.servername, &adr))
472 {
473 Com_Printf ("Bad server address\n");
474 cls.state = ca_disconnected;
475 return;
476 }
477 if (adr.port == 0)
478 adr.port = BigShort (PORT_SERVER);
479
480 cls.connect_time = cls.realtime; // for retransmit requests
481
482 Com_Printf ("Connecting to %s...\n", cls.servername);
483
484 Netchan_OutOfBandPrint (NS_CLIENT, adr, "getchallenge\n");
485 }
486
487
488 /*
489 ================
490 CL_Connect_f
491
492 ================
493 */
494 void CL_Connect_f (void)
495 {
496 char *server;
497
498 if (Cmd_Argc() != 2)
499 {
500 Com_Printf ("usage: connect <server>\n");
501 return;
502 }
503
504 if (Com_ServerState ())
505 { // if running a local server, kill it and reissue
506 SV_Shutdown (va("Server quit\n", msg), false);
507 }
508 else
509 {
510 CL_Disconnect ();
511 }
512
513 server = Cmd_Argv (1);
514
515 NET_Config (true); // allow remote
516
517 CL_Disconnect ();
518
519 cls.state = ca_connecting;
520 strncpy (cls.servername, server, sizeof(cls.servername)-1);
521 cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
522 }
523
524
525 /*
526 =====================
527 CL_Rcon_f
528
529 Send the rest of the command line over as
530 an unconnected command.
531 =====================
532 */
533 void CL_Rcon_f (void)
534 {
535 char message[1024];
536 int i;
537 netadr_t to;
538
539 if (!rcon_client_password->string)
540 {
541 Com_Printf ("You must set 'rcon_password' before\n"
542 "issuing an rcon command.\n");
543 return;
544 }
545
546 message[0] = (char)255;
547 message[1] = (char)255;
548 message[2] = (char)255;
549 message[3] = (char)255;
550 message[4] = 0;
551
552 NET_Config (true); // allow remote
553
554 strcat (message, "rcon ");
555
556 strcat (message, rcon_client_password->string);
557 strcat (message, " ");
558
559 for (i=1 ; i<Cmd_Argc() ; i++)
560 {
561 strcat (message, Cmd_Argv(i));
562 strcat (message, " ");
563 }
564
565 if (cls.state >= ca_connected)
566 to = cls.netchan.remote_address;
567 else
568 {
569 if (!strlen(rcon_address->string))
570 {
571 Com_Printf ("You must either be connected,\n"
572 "or set the 'rcon_address' cvar\n"
573 "to issue rcon commands\n");
574
575 return;
576 }
577 NET_StringToAdr (rcon_address->string, &to);
578 if (to.port == 0)
579 to.port = BigShort (PORT_SERVER);
580 }
581
582 NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
583 }
584
585
586 /*
587 =====================
588 CL_ClearState
589
590 =====================
591 */
592 void CL_ClearState (void)
593 {
594 S_StopAllSounds ();
595 CL_ClearEffects ();
596 CL_ClearTEnts ();
597
598 // wipe the entire cl structure
599 memset (&cl, 0, sizeof(cl));
600 memset (&cl_entities, 0, sizeof(cl_entities));
601
602 SZ_Clear (&cls.netchan.message);
603
604 }
605
606 /*
607 =====================
608 CL_Disconnect
609
610 Goes from a connected state to full screen console state
611 Sends a disconnect message to the server
612 This is also called on Com_Error, so it shouldn't cause any errors
613 =====================
614 */
615 void CL_Disconnect (void)
616 {
617 byte final[32];
618
619 if (cls.state == ca_disconnected)
620 return;
621
622 if (cl_timedemo && cl_timedemo->value)
623 {
624 int time;
625
626 time = Sys_Milliseconds () - cl.timedemo_start;
627 if (time > 0)
628 Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames,
629 time/1000.0, cl.timedemo_frames*1000.0 / time);
630 }
631
632 VectorClear (cl.refdef.blend);
633 re.CinematicSetPalette(NULL);
634
635 M_ForceMenuOff ();
636
637 cls.connect_time = 0;
638
639 SCR_StopCinematic ();
640
641 if (cls.demorecording)
642 CL_Stop_f ();
643
644 // send a disconnect message to the server
645 final[0] = clc_stringcmd;
646 strcpy ((char *)final+1, "disconnect");
647 Netchan_Transmit (&cls.netchan, strlen(final), final);
648 Netchan_Transmit (&cls.netchan, strlen(final), final);
649 Netchan_Transmit (&cls.netchan, strlen(final), final);
650
651 CL_ClearState ();
652
653 // stop download
654 if (cls.download) {
655 fclose(cls.download);
656 cls.download = NULL;
657 }
658
659 cls.state = ca_disconnected;
660 }
661
662 void CL_Disconnect_f (void)
663 {
664 Com_Error (ERR_DROP, "Disconnected from server");
665 }
666
667
668 /*
669 ====================
670 CL_Packet_f
671
672 packet <destination> <contents>
673
674 Contents allows \n escape character
675 ====================
676 */
677 void CL_Packet_f (void)
678 {
679 char send[2048];
680 int i, l;
681 char *in, *out;
682 netadr_t adr;
683
684 if (Cmd_Argc() != 3)
685 {
686 Com_Printf ("packet <destination> <contents>\n");
687 return;
688 }
689
690 NET_Config (true); // allow remote
691
692 if (!NET_StringToAdr (Cmd_Argv(1), &adr))
693 {
694 Com_Printf ("Bad address\n");
695 return;
696 }
697 if (!adr.port)
698 adr.port = BigShort (PORT_SERVER);
699
700 in = Cmd_Argv(2);
701 out = send+4;
702 send[0] = send[1] = send[2] = send[3] = (char)0xff;
703
704 l = strlen (in);
705 for (i=0 ; i<l ; i++)
706 {
707 if (in[i] == '\\' && in[i+1] == 'n')
708 {
709 *out++ = '\n';
710 i++;
711 }
712 else
713 *out++ = in[i];
714 }
715 *out = 0;
716
717 NET_SendPacket (NS_CLIENT, out-send, send, adr);
718 }
719
720 /*
721 =================
722 CL_Changing_f
723
724 Just sent as a hint to the client that they should
725 drop to full console
726 =================
727 */
728 void CL_Changing_f (void)
729 {
730 //ZOID
731 //if we are downloading, we don't change! This so we don't suddenly stop downloading a map
732 if (cls.download)
733 return;
734
735 SCR_BeginLoadingPlaque ();
736 cls.state = ca_connected; // not active anymore, but not disconnected
737 Com_Printf ("\nChanging map...\n");
738 }
739
740
741 /*
742 =================
743 CL_Reconnect_f
744
745 The server is changing levels
746 =================
747 */
748 void CL_Reconnect_f (void)
749 {
750 //ZOID
751 //if we are downloading, we don't change! This so we don't suddenly stop downloading a map
752 if (cls.download)
753 return;
754
755 S_StopAllSounds ();
756 if (cls.state == ca_connected) {
757 Com_Printf ("reconnecting...\n");
758 cls.state = ca_connected;
759 MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
760 MSG_WriteString (&cls.netchan.message, "new");
761 return;
762 }
763
764 if (*cls.servername) {
765 if (cls.state >= ca_connected) {
766 CL_Disconnect();
767 cls.connect_time = cls.realtime - 1500;
768 } else
769 cls.connect_time = -99999; // fire immediately
770
771 cls.state = ca_connecting;
772 Com_Printf ("reconnecting...\n");
773 }
774 }
775
776 /*
777 =================
778 CL_ParseStatusMessage
779
780 Handle a reply from a ping
781 =================
782 */
783 void CL_ParseStatusMessage (void)
784 {
785 char *s;
786
787 s = MSG_ReadString(&net_message);
788
789 Com_Printf ("%s\n", s);
790 M_AddToServerList (net_from, s);
791 }
792
793
794 /*
795 =================
796 CL_PingServers_f
797 =================
798 */
799 void CL_PingServers_f (void)
800 {
801 int i;
802 netadr_t adr;
803 char name[32];
804 char *adrstring;
805 cvar_t *noudp;
806 cvar_t *noipx;
807
808 NET_Config (true); // allow remote
809
810 // send a broadcast packet
811 Com_Printf ("pinging broadcast...\n");
812
813 noudp = Cvar_Get ("noudp", "0", CVAR_NOSET);
814 if (!noudp->value)
815 {
816 adr.type = NA_BROADCAST;
817 adr.port = BigShort(PORT_SERVER);
818 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
819 }
820
821 noipx = Cvar_Get ("noipx", "0", CVAR_NOSET);
822 if (!noipx->value)
823 {
824 adr.type = NA_BROADCAST_IPX;
825 adr.port = BigShort(PORT_SERVER);
826 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
827 }
828
829 // send a packet to each address book entry
830 for (i=0 ; i<16 ; i++)
831 {
832 Com_sprintf (name, sizeof(name), "adr%i", i);
833 adrstring = Cvar_VariableString (name);
834 if (!adrstring || !adrstring[0])
835 continue;
836
837 Com_Printf ("pinging %s...\n", adrstring);
838 if (!NET_StringToAdr (adrstring, &adr))
839 {
840 Com_Printf ("Bad address: %s\n", adrstring);
841 continue;
842 }
843 if (!adr.port)
844 adr.port = BigShort(PORT_SERVER);
845 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
846 }
847 }
848
849
850 /*
851 =================
852 CL_Skins_f
853
854 Load or download any custom player skins and models
855 =================
856 */
857 void CL_Skins_f (void)
858 {
859 int i;
860
861 for (i=0 ; i<MAX_CLIENTS ; i++)
862 {
863 if (!cl.configstrings[CS_PLAYERSKINS+i][0])
864 continue;
865 Com_Printf ("client %i: %s\n", i, cl.configstrings[CS_PLAYERSKINS+i]);
866 SCR_UpdateScreen ();
867 Sys_SendKeyEvents (); // pump message loop
868 CL_ParseClientinfo (i);
869 }
870 }
871
872
873 /*
874 =================
875 CL_ConnectionlessPacket
876
877 Responses to broadcasts, etc
878 =================
879 */
880 void CL_ConnectionlessPacket (void)
881 {
882 char *s;
883 char *c;
884
885 MSG_BeginReading (&net_message);
886 MSG_ReadLong (&net_message); // skip the -1
887
888 s = MSG_ReadStringLine (&net_message);
889
890 Cmd_TokenizeString (s, false);
891
892 c = Cmd_Argv(0);
893
894 Com_Printf ("%s: %s\n", NET_AdrToString (net_from), c);
895
896 // server connection
897 if (!strcmp(c, "client_connect"))
898 {
899 if (cls.state == ca_connected)
900 {
901 Com_Printf ("Dup connect received. Ignored.\n");
902 return;
903 }
904 Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, cls.quakePort);
905 MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
906 MSG_WriteString (&cls.netchan.message, "new");
907 cls.state = ca_connected;
908 return;
909 }
910
911 // server responding to a status broadcast
912 if (!strcmp(c, "info"))
913 {
914 CL_ParseStatusMessage ();
915 return;
916 }
917
918 // remote command from gui front end
919 if (!strcmp(c, "cmd"))
920 {
921 if (!NET_IsLocalAddress(net_from))
922 {
923 Com_Printf ("Command packet from remote host. Ignored.\n");
924 return;
925 }
926 Sys_AppActivate ();
927 s = MSG_ReadString (&net_message);
928 Cbuf_AddText (s);
929 Cbuf_AddText ("\n");
930 return;
931 }
932 // print command from somewhere
933 if (!strcmp(c, "print"))
934 {
935 s = MSG_ReadString (&net_message);
936 Com_Printf ("%s", s);
937 return;
938 }
939
940 // ping from somewhere
941 if (!strcmp(c, "ping"))
942 {
943 Netchan_OutOfBandPrint (NS_CLIENT, net_from, "ack");
944 return;
945 }
946
947 // challenge from the server we are connecting to
948 if (!strcmp(c, "challenge"))
949 {
950 cls.challenge = atoi(Cmd_Argv(1));
951 CL_SendConnectPacket ();
952 return;
953 }
954
955 // echo request from server
956 if (!strcmp(c, "echo"))
957 {
958 Netchan_OutOfBandPrint (NS_CLIENT, net_from, "%s", Cmd_Argv(1) );
959 return;
960 }
961
962 Com_Printf ("Unknown command.\n");
963 }
964
965
966 /*
967 =================
968 CL_DumpPackets
969
970 A vain attempt to help bad TCP stacks that cause problems
971 when they overflow
972 =================
973 */
974 void CL_DumpPackets (void)
975 {
976 while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
977 {
978 Com_Printf ("dumnping a packet\n");
979 }
980 }
981
982 /*
983 =================
984 CL_ReadPackets
985 =================
986 */
987 void CL_ReadPackets (void)
988 {
989 while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
990 {
991 // Com_Printf ("packet\n");
992 //
993 // remote command packet
994 //
995 if (*(int *)net_message.data == -1)
996 {
997 CL_ConnectionlessPacket ();
998 continue;
999 }
1000
1001 if (cls.state == ca_disconnected || cls.state == ca_connecting)
1002 continue; // dump it if not connected
1003
1004 if (net_message.cursize < 8)
1005 {
1006 Com_Printf ("%s: Runt packet\n",NET_AdrToString(net_from));
1007 continue;
1008 }
1009
1010 //
1011 // packet from server
1012 //
1013 if (!NET_CompareAdr (net_from, cls.netchan.remote_address))
1014 {
1015 Com_DPrintf ("%s:sequenced packet without connection\n"
1016 ,NET_AdrToString(net_from));
1017 continue;
1018 }
1019 if (!Netchan_Process(&cls.netchan, &net_message))
1020 continue; // wasn't accepted for some reason
1021 CL_ParseServerMessage ();
1022 }
1023
1024 //
1025 // check timeout
1026 //
1027 if (cls.state >= ca_connected
1028 && cls.realtime - cls.netchan.last_received > cl_timeout->value*1000)
1029 {
1030 if (++cl.timeoutcount > 5) // timeoutcount saves debugger
1031 {
1032 Com_Printf ("\nServer connection timed out.\n");
1033 CL_Disconnect ();
1034 return;
1035 }
1036 }
1037 else
1038 cl.timeoutcount = 0;
1039
1040 }
1041
1042
1043 //=============================================================================
1044
1045 /*
1046 ==============
1047 CL_FixUpGender_f
1048 ==============
1049 */
1050 void CL_FixUpGender(void)
1051 {
1052 char *p;
1053 char sk[80];
1054
1055 if (gender_auto->value) {
1056
1057 if (gender->modified) {
1058 // was set directly, don't override the user
1059 gender->modified = false;
1060 return;
1061 }
1062
1063 strncpy(sk, skin->string, sizeof(sk) - 1);
1064 if ((p = strchr(sk, '/')) != NULL)
1065 *p = 0;
1066 if (Q_stricmp(sk, "male") == 0 || Q_stricmp(sk, "cyborg") == 0)
1067 Cvar_Set ("gender", "male");
1068 else if (Q_stricmp(sk, "female") == 0 || Q_stricmp(sk, "crackhor") == 0)
1069 Cvar_Set ("gender", "female");
1070 else
1071 Cvar_Set ("gender", "none");
1072 gender->modified = false;
1073 }
1074 }
1075
1076 /*
1077 ==============
1078 CL_Userinfo_f
1079 ==============
1080 */
1081 void CL_Userinfo_f (void)
1082 {
1083 Com_Printf ("User info settings:\n");
1084 Info_Print (Cvar_Userinfo());
1085 }
1086
1087 /*
1088 =================
1089 CL_Snd_Restart_f
1090
1091 Restart the sound subsystem so it can pick up
1092 new parameters and flush all sounds
1093 =================
1094 */
1095 void CL_Snd_Restart_f (void)
1096 {
1097 S_Shutdown ();
1098 S_Init ();
1099 CL_RegisterSounds ();
1100 }
1101
1102 int precache_check; // for autodownload of precache items
1103 int precache_spawncount;
1104 int precache_tex;
1105 int precache_model_skin;
1106
1107 byte *precache_model; // used for skin checking in alias models
1108
1109 #define PLAYER_MULT 5
1110
1111 // ENV_CNT is map load, ENV_CNT+1 is first env map
1112 #define ENV_CNT (CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
1113 #define TEXTURE_CNT (ENV_CNT+13)
1114
1115 static const char *env_suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
1116
1117 void CL_RequestNextDownload (void)
1118 {
1119 unsigned map_checksum; // for detecting cheater maps
1120 char fn[MAX_OSPATH];
1121 dmdl_t *pheader;
1122
1123 if (cls.state != ca_connected)
1124 return;
1125
1126 if (!allow_download->value && precache_check < ENV_CNT)
1127 precache_check = ENV_CNT;
1128
1129 //ZOID
1130 if (precache_check == CS_MODELS) { // confirm map
1131 precache_check = CS_MODELS+2; // 0 isn't used
1132 if (allow_download_maps->value)
1133 if (!CL_CheckOrDownloadFile(cl.configstrings[CS_MODELS+1]))
1134 return; // started a download
1135 }
1136 if (precache_check >= CS_MODELS && precache_check < CS_MODELS+MAX_MODELS) {
1137 if (allow_download_models->value) {
1138 while (precache_check < CS_MODELS+MAX_MODELS &&
1139 cl.configstrings[precache_check][0]) {
1140 if (cl.configstrings[precache_check][0] == '*' ||
1141 cl.configstrings[precache_check][0] == '#') {
1142 precache_check++;
1143 continue;
1144 }
1145 if (precache_model_skin == 0) {
1146 if (!CL_CheckOrDownloadFile(cl.configstrings[precache_check])) {
1147 precache_model_skin = 1;
1148 return; // started a download
1149 }
1150 precache_model_skin = 1;
1151 }
1152
1153 // checking for skins in the model
1154 if (!precache_model) {
1155
1156 FS_LoadFile (cl.configstrings[precache_check], (void **)&precache_model);
1157 if (!precache_model) {
1158 precache_model_skin = 0;
1159 precache_check++;
1160 continue; // couldn't load it
1161 }
1162 if (LittleLong(*(unsigned *)precache_model) != IDALIASHEADER) {
1163 // not an alias model
1164 FS_FreeFile(precache_model);
1165 precache_model = 0;
1166 precache_model_skin = 0;
1167 precache_check++;
1168 continue;
1169 }
1170 pheader = (dmdl_t *)precache_model;
1171 if (LittleLong (pheader->version) != ALIAS_VERSION) {
1172 precache_check++;
1173 precache_model_skin = 0;
1174 continue; // couldn't load it
1175 }
1176 }
1177
1178 pheader = (dmdl_t *)precache_model;
1179
1180 while (precache_model_skin - 1 < LittleLong(pheader->num_skins)) {
1181 if (!CL_CheckOrDownloadFile((char *)precache_model +
1182 LittleLong(pheader->ofs_skins) +
1183 (precache_model_skin - 1)*MAX_SKINNAME)) {
1184 precache_model_skin++;
1185 return; // started a download
1186 }
1187 precache_model_skin++;
1188 }
1189 if (precache_model) {
1190 FS_FreeFile(precache_model);
1191 precache_model = 0;
1192 }
1193 precache_model_skin = 0;
1194 precache_check++;
1195 }
1196 }
1197 precache_check = CS_SOUNDS;
1198 }
1199 if (precache_check >= CS_SOUNDS && precache_check < CS_SOUNDS+MAX_SOUNDS) {
1200 if (allow_download_sounds->value) {
1201 if (precache_check == CS_SOUNDS)
1202 precache_check++; // zero is blank
1203 while (precache_check < CS_SOUNDS+MAX_SOUNDS &&
1204 cl.configstrings[precache_check][0]) {
1205 if (cl.configstrings[precache_check][0] == '*') {
1206 precache_check++;
1207 continue;
1208 }
1209 Com_sprintf(fn, sizeof(fn), "sound/%s", cl.configstrings[precache_check++]);
1210 if (!CL_CheckOrDownloadFile(fn))
1211 return; // started a download
1212 }
1213 }
1214 precache_check = CS_IMAGES;
1215 }
1216 if (precache_check >= CS_IMAGES && precache_check < CS_IMAGES+MAX_IMAGES) {
1217 if (precache_check == CS_IMAGES)
1218 precache_check++; // zero is blank
1219 while (precache_check < CS_IMAGES+MAX_IMAGES &&
1220 cl.configstrings[precache_check][0]) {
1221 Com_sprintf(fn, sizeof(fn), "pics/%s.pcx", cl.configstrings[precache_check++]);
1222 if (!CL_CheckOrDownloadFile(fn))
1223 return; // started a download
1224 }
1225 precache_check = CS_PLAYERSKINS;
1226 }
1227 // skins are special, since a player has three things to download:
1228 // model, weapon model and skin
1229 // so precache_check is now *3
1230 if (precache_check >= CS_PLAYERSKINS && precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) {
1231 if (allow_download_players->value) {
1232 while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) {
1233 int i, n;
1234 char model[MAX_QPATH], skin[MAX_QPATH], *p;
1235
1236 i = (precache_check - CS_PLAYERSKINS)/PLAYER_MULT;
1237 n = (precache_check - CS_PLAYERSKINS)%PLAYER_MULT;
1238
1239 if (!cl.configstrings[CS_PLAYERSKINS+i][0]) {
1240 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
1241 continue;
1242 }
1243
1244 if ((p = strchr(cl.configstrings[CS_PLAYERSKINS+i], '\\')) != NULL)
1245 p++;
1246 else
1247 p = cl.configstrings[CS_PLAYERSKINS+i];
1248 strcpy(model, p);
1249 p = strchr(model, '/');
1250 if (!p)
1251 p = strchr(model, '\\');
1252 if (p) {
1253 *p++ = 0;
1254 strcpy(skin, p);
1255 } else
1256 *skin = 0;
1257
1258 switch (n) {
1259 case 0: // model
1260 Com_sprintf(fn, sizeof(fn), "players/%s/tris.md2", model);
1261 if (!CL_CheckOrDownloadFile(fn)) {
1262 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1;
1263 return; // started a download
1264 }
1265 n++;
1266 /*FALL THROUGH*/
1267
1268 case 1: // weapon model
1269 Com_sprintf(fn, sizeof(fn), "players/%s/weapon.md2", model);
1270 if (!CL_CheckOrDownloadFile(fn)) {
1271 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2;
1272 return; // started a download
1273 }
1274 n++;
1275 /*FALL THROUGH*/
1276
1277 case 2: // weapon skin
1278 Com_sprintf(fn, sizeof(fn), "players/%s/weapon.pcx", model);
1279 if (!CL_CheckOrDownloadFile(fn)) {
1280 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3;
1281 return; // started a download
1282 }
1283 n++;
1284 /*FALL THROUGH*/
1285
1286 case 3: // skin
1287 Com_sprintf(fn, sizeof(fn), "players/%s/%s.pcx", model, skin);
1288 if (!CL_CheckOrDownloadFile(fn)) {
1289 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4;
1290 return; // started a download
1291 }
1292 n++;
1293 /*FALL THROUGH*/
1294
1295 case 4: // skin_i
1296 Com_sprintf(fn, sizeof(fn), "players/%s/%s_i.pcx", model, skin);
1297 if (!CL_CheckOrDownloadFile(fn)) {
1298 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5;
1299 return; // started a download
1300 }
1301 // move on to next model
1302 precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
1303 }
1304 }
1305 }
1306 // precache phase completed
1307 precache_check = ENV_CNT;
1308 }
1309
1310 if (precache_check == ENV_CNT) {
1311 precache_check = ENV_CNT + 1;
1312
1313 CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
1314
1315 if (map_checksum != atoi(cl.configstrings[CS_MAPCHECKSUM])) {
1316 Com_Error (ERR_DROP, "Local map version differs from server: %i != '%s'\n",
1317 map_checksum, cl.configstrings[CS_MAPCHECKSUM]);
1318 return;
1319 }
1320 }
1321
1322 if (precache_check > ENV_CNT && precache_check < TEXTURE_CNT) {
1323 if (allow_download->value && allow_download_maps->value) {
1324 while (precache_check < TEXTURE_CNT) {
1325 int n = precache_check++ - ENV_CNT - 1;
1326
1327 if (n & 1)
1328 Com_sprintf(fn, sizeof(fn), "env/%s%s.pcx",
1329 cl.configstrings[CS_SKY], env_suf[n/2]);
1330 else
1331 Com_sprintf(fn, sizeof(fn), "env/%s%s.tga",
1332 cl.configstrings[CS_SKY], env_suf[n/2]);
1333 if (!CL_CheckOrDownloadFile(fn))
1334 return; // started a download
1335 }
1336 }
1337 precache_check = TEXTURE_CNT;
1338 }
1339
1340 if (precache_check == TEXTURE_CNT) {
1341 precache_check = TEXTURE_CNT+1;
1342 precache_tex = 0;
1343 }
1344
1345 // confirm existance of textures, download any that don't exist
1346 if (precache_check == TEXTURE_CNT+1) {
1347 // from qcommon/cmodel.c
1348 extern int numtexinfo;
1349 extern mapsurface_t map_surfaces[];
1350
1351 if (allow_download->value && allow_download_maps->value) {
1352 while (precache_tex < numtexinfo) {
1353 char fn[MAX_OSPATH];
1354
1355 sprintf(fn, "textures/%s.wal", map_surfaces[precache_tex++].rname);
1356 if (!CL_CheckOrDownloadFile(fn))
1357 return; // started a download
1358 }
1359 }
1360 precache_check = TEXTURE_CNT+999;
1361 }
1362
1363 //ZOID
1364 CL_RegisterSounds ();
1365 CL_PrepRefresh ();
1366
1367 MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
1368 MSG_WriteString (&cls.netchan.message, va("begin %i\n", precache_spawncount) );
1369 }
1370
1371 /*
1372 =================
1373 CL_Precache_f
1374
1375 The server will send this command right
1376 before allowing the client into the server
1377 =================
1378 */
1379 void CL_Precache_f (void)
1380 {
1381 //Yet another hack to let old demos work
1382 //the old precache sequence
1383 if (Cmd_Argc() < 2) {
1384 unsigned map_checksum; // for detecting cheater maps
1385
1386 CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
1387 CL_RegisterSounds ();
1388 CL_PrepRefresh ();
1389 return;
1390 }
1391
1392 precache_check = CS_MODELS;
1393 precache_spawncount = atoi(Cmd_Argv(1));
1394 precache_model = 0;
1395 precache_model_skin = 0;
1396
1397 CL_RequestNextDownload();
1398 }
1399
1400
1401 /*
1402 =================
1403 CL_InitLocal
1404 =================
1405 */
1406 void CL_InitLocal (void)
1407 {
1408 cls.state = ca_disconnected;
1409 cls.realtime = Sys_Milliseconds ();
1410
1411 CL_InitInput ();
1412
1413 adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE );
1414 adr1 = Cvar_Get( "adr1", "", CVAR_ARCHIVE );
1415 adr2 = Cvar_Get( "adr2", "", CVAR_ARCHIVE );
1416 adr3 = Cvar_Get( "adr3", "", CVAR_ARCHIVE );
1417 adr4 = Cvar_Get( "adr4", "", CVAR_ARCHIVE );
1418 adr5 = Cvar_Get( "adr5", "", CVAR_ARCHIVE );
1419 adr6 = Cvar_Get( "adr6", "", CVAR_ARCHIVE );
1420 adr7 = Cvar_Get( "adr7", "", CVAR_ARCHIVE );
1421 adr8 = Cvar_Get( "adr8", "", CVAR_ARCHIVE );
1422
1423 //
1424 // register our variables
1425 //
1426 cl_stereo_separation = Cvar_Get( "cl_stereo_separation", "0.4", CVAR_ARCHIVE );
1427 cl_stereo = Cvar_Get( "cl_stereo", "0", 0 );
1428
1429 cl_add_blend = Cvar_Get ("cl_blend", "1", 0);
1430 cl_add_lights = Cvar_Get ("cl_lights", "1", 0);
1431 cl_add_particles = Cvar_Get ("cl_particles", "1", 0);
1432 cl_add_entities = Cvar_Get ("cl_entities", "1", 0);
1433 cl_gun = Cvar_Get ("cl_gun", "1", 0);
1434 cl_footsteps = Cvar_Get ("cl_footsteps", "1", 0);
1435 cl_noskins = Cvar_Get ("cl_noskins", "0", 0);
1436 cl_autoskins = Cvar_Get ("cl_autoskins", "0", 0);
1437 cl_predict = Cvar_Get ("cl_predict", "1", 0);
1438 // cl_minfps = Cvar_Get ("cl_minfps", "5", 0);
1439 cl_maxfps = Cvar_Get ("cl_maxfps", "90", 0);
1440
1441 cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0);
1442 cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0);
1443 cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0);
1444 cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", 0);
1445 cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", 0);
1446 cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
1447
1448 cl_run = Cvar_Get ("cl_run", "0", CVAR_ARCHIVE);
1449 freelook = Cvar_Get( "freelook", "0", CVAR_ARCHIVE );
1450 lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE);
1451 lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE);
1452 sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE);
1453
1454 m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
1455 m_yaw = Cvar_Get ("m_yaw", "0.022", 0);
1456 m_forward = Cvar_Get ("m_forward", "1", 0);
1457 m_side = Cvar_Get ("m_side", "1", 0);
1458
1459 cl_shownet = Cvar_Get ("cl_shownet", "0", 0);
1460 cl_showmiss = Cvar_Get ("cl_showmiss", "0", 0);
1461 cl_showclamp = Cvar_Get ("showclamp", "0", 0);
1462 cl_timeout = Cvar_Get ("cl_timeout", "120", 0);
1463 cl_paused = Cvar_Get ("paused", "0", 0);
1464 cl_timedemo = Cvar_Get ("timedemo", "0", 0);
1465
1466 rcon_client_password = Cvar_Get ("rcon_password", "", 0);
1467 rcon_address = Cvar_Get ("rcon_address", "", 0);
1468
1469 cl_lightlevel = Cvar_Get ("r_lightlevel", "0", 0);
1470
1471 //
1472 // userinfo
1473 //
1474 info_password = Cvar_Get ("password", "", CVAR_USERINFO);
1475 info_spectator = Cvar_Get ("spectator", "0", CVAR_USERINFO);
1476 name = Cvar_Get ("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE);
1477 skin = Cvar_Get ("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE);
1478 rate = Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE); // FIXME
1479 msg = Cvar_Get ("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE);
1480 hand = Cvar_Get ("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
1481 fov = Cvar_Get ("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE);
1482 gender = Cvar_Get ("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE);
1483 gender_auto = Cvar_Get ("gender_auto", "1", CVAR_ARCHIVE);
1484 gender->modified = false; // clear this so we know when user sets it manually
1485
1486 cl_vwep = Cvar_Get ("cl_vwep", "1", CVAR_ARCHIVE);
1487
1488
1489 //
1490 // register our commands
1491 //
1492 Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
1493 Cmd_AddCommand ("pause", CL_Pause_f);
1494 Cmd_AddCommand ("pingservers", CL_PingServers_f);
1495 Cmd_AddCommand ("skins", CL_Skins_f);
1496
1497 Cmd_AddCommand ("userinfo", CL_Userinfo_f);
1498 Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
1499
1500 Cmd_AddCommand ("changing", CL_Changing_f);
1501 Cmd_AddCommand ("disconnect", CL_Disconnect_f);
1502 Cmd_AddCommand ("record", CL_Record_f);
1503 Cmd_AddCommand ("stop", CL_Stop_f);
1504
1505 Cmd_AddCommand ("quit", CL_Quit_f);
1506
1507 Cmd_AddCommand ("connect", CL_Connect_f);
1508 Cmd_AddCommand ("reconnect", CL_Reconnect_f);
1509
1510 Cmd_AddCommand ("rcon", CL_Rcon_f);
1511
1512 // Cmd_AddCommand ("packet", CL_Packet_f); // this is dangerous to leave in
1513
1514 Cmd_AddCommand ("setenv", CL_Setenv_f );
1515
1516 Cmd_AddCommand ("precache", CL_Precache_f);
1517
1518 Cmd_AddCommand ("download", CL_Download_f);
1519
1520 //
1521 // forward to server commands
1522 //
1523 // the only thing this does is allow command completion
1524 // to work -- all unknown commands are automatically
1525 // forwarded to the server
1526 Cmd_AddCommand ("wave", NULL);
1527 Cmd_AddCommand ("inven", NULL);
1528 Cmd_AddCommand ("kill", NULL);
1529 Cmd_AddCommand ("use", NULL);
1530 Cmd_AddCommand ("drop", NULL);
1531 Cmd_AddCommand ("say", NULL);
1532 Cmd_AddCommand ("say_team", NULL);
1533 Cmd_AddCommand ("info", NULL);
1534 Cmd_AddCommand ("prog", NULL);
1535 Cmd_AddCommand ("give", NULL);
1536 Cmd_AddCommand ("god", NULL);
1537 Cmd_AddCommand ("notarget", NULL);
1538 Cmd_AddCommand ("noclip", NULL);
1539 Cmd_AddCommand ("invuse", NULL);
1540 Cmd_AddCommand ("invprev", NULL);
1541 Cmd_AddCommand ("invnext", NULL);
1542 Cmd_AddCommand ("invdrop", NULL);
1543 Cmd_AddCommand ("weapnext", NULL);
1544 Cmd_AddCommand ("weapprev", NULL);
1545 }
1546
1547
1548
1549 /*
1550 ===============
1551 CL_WriteConfiguration
1552
1553 Writes key bindings and archived cvars to config.cfg
1554 ===============
1555 */
1556 void CL_WriteConfiguration (void)
1557 {
1558 FILE *f;
1559 char path[MAX_QPATH];
1560
1561 if (cls.state == ca_uninitialized)
1562 return;
1563
1564 Com_sprintf (path, sizeof(path),"%s/config.cfg",FS_Gamedir());
1565 f = fopen (path, "w");
1566 if (!f)
1567 {
1568 Com_Printf ("Couldn't write config.cfg.\n");
1569 return;
1570 }
1571
1572 fprintf (f, "// generated by quake, do not modify\n");
1573 Key_WriteBindings (f);
1574 fclose (f);
1575
1576 Cvar_WriteVariables (path);
1577 }
1578
1579
1580 /*
1581 ==================
1582 CL_FixCvarCheats
1583
1584 ==================
1585 */
1586
1587 typedef struct
1588 {
1589 char *name;
1590 char *value;
1591 cvar_t *var;
1592 } cheatvar_t;
1593
1594 cheatvar_t cheatvars[] = {
1595 {"timescale", "1"},
1596 {"timedemo", "0"},
1597 {"r_drawworld", "1"},
1598 {"cl_testlights", "0"},
1599 {"r_fullbright", "0"},
1600 {"r_drawflat", "0"},
1601 {"paused", "0"},
1602 {"fixedtime", "0"},
1603 {"sw_draworder", "0"},
1604 {"gl_lightmap", "0"},
1605 {"gl_saturatelighting", "0"},
1606 {NULL, NULL}
1607 };
1608
1609 int numcheatvars;
1610
1611 void CL_FixCvarCheats (void)
1612 {
1613 int i;
1614 cheatvar_t *var;
1615
1616 if ( !strcmp(cl.configstrings[CS_MAXCLIENTS], "1")
1617 || !cl.configstrings[CS_MAXCLIENTS][0] )
1618 return; // single player can cheat
1619
1620 // find all the cvars if we haven't done it yet
1621 if (!numcheatvars)
1622 {
1623 while (cheatvars[numcheatvars].name)
1624 {
1625 cheatvars[numcheatvars].var = Cvar_Get (cheatvars[numcheatvars].name,
1626 cheatvars[numcheatvars].value, 0);
1627 numcheatvars++;
1628 }
1629 }
1630
1631 // make sure they are all set to the proper values
1632 for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
1633 {
1634 if ( strcmp (var->var->string, var->value) )
1635 {
1636 Cvar_Set (var->name, var->value);
1637 }
1638 }
1639 }
1640
1641 //============================================================================
1642
1643 /*
1644 ==================
1645 CL_SendCommand
1646
1647 ==================
1648 */
1649 void CL_SendCommand (void)
1650 {
1651 // get new key events
1652 Sys_SendKeyEvents ();
1653
1654 // allow mice or other external controllers to add commands
1655 IN_Commands ();
1656
1657 // process console commands
1658 Cbuf_Execute ();
1659
1660 // fix any cheating cvars
1661 CL_FixCvarCheats ();
1662
1663 // send intentions now
1664 CL_SendCmd ();
1665
1666 // resend a connection request if necessary
1667 CL_CheckForResend ();
1668 }
1669
1670
1671 /*
1672 ==================
1673 CL_Frame
1674
1675 ==================
1676 */
1677 void CL_Frame (int msec)
1678 {
1679 static int extratime;
1680 static int lasttimecalled;
1681
1682 if (dedicated->value)
1683 return;
1684
1685 extratime += msec;
1686
1687 if (!cl_timedemo->value)
1688 {
1689 if (cls.state == ca_connected && extratime < 100)
1690 return; // don't flood packets out while connecting
1691 if (extratime < 1000/cl_maxfps->value)
1692 return; // framerate is too high
1693 }
1694
1695 // let the mouse activate or deactivate
1696 IN_Frame ();
1697
1698 // decide the simulation time
1699 cls.frametime = extratime/1000.0;
1700 cl.time += extratime;
1701 cls.realtime = curtime;
1702
1703 extratime = 0;
1704 #if 0
1707 #else
1708 if (cls.frametime > (1.0 / 5))
1709 cls.frametime = (1.0 / 5);
1710 #endif
1711
1712 // if in the debugger last frame, don't timeout
1713 if (msec > 5000)
1714 cls.netchan.last_received = Sys_Milliseconds ();
1715
1716 // fetch results from server
1717 CL_ReadPackets ();
1718
1719 // send a new command message to the server
1720 CL_SendCommand ();
1721
1722 // predict all unacknowledged movements
1723 CL_PredictMovement ();
1724
1725 // allow rendering DLL change
1726 VID_CheckChanges ();
1727 if (!cl.refresh_prepped && cls.state == ca_active)
1728 CL_PrepRefresh ();
1729
1730 // update the screen
1731 if (host_speeds->value)
1732 time_before_ref = Sys_Milliseconds ();
1733 SCR_UpdateScreen ();
1734 if (host_speeds->value)
1735 time_after_ref = Sys_Milliseconds ();
1736
1737 // update audio
1738 S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up);
1739
1740 CDAudio_Update();
1741
1742 // advance local effects for next frame
1743 CL_RunDLights ();
1744 CL_RunLightStyles ();
1745 SCR_RunCinematic ();
1746 SCR_RunConsole ();
1747
1748 cls.framecount++;
1749
1750 if ( log_stats->value )
1751 {
1752 if ( cls.state == ca_active )
1753 {
1754 if ( !lasttimecalled )
1755 {
1756 lasttimecalled = Sys_Milliseconds();
1757 if ( log_stats_file )
1758 fprintf( log_stats_file, "0\n" );
1759 }
1760 else
1761 {
1762 int now = Sys_Milliseconds();
1763
1764 if ( log_stats_file )
1765 fprintf( log_stats_file, "%d\n", now - lasttimecalled );
1766 lasttimecalled = now;
1767 }
1768 }
1769 }
1770 }
1771
1772
1773 //============================================================================
1774
1775 /*
1776 ====================
1777 CL_Init
1778 ====================
1779 */
1780 void CL_Init (void)
1781 {
1782 if (dedicated->value)
1783 return; // nothing running on the client
1784
1785 // all archived variables will now be loaded
1786
1787 Con_Init ();
1788 #if defined __linux__ || defined __sgi
1791 #else
1792 VID_Init ();
1793 S_Init (); // sound must be initialized after window is created
1794 #endif
1795
1796 V_Init ();
1797
1798 net_message.data = net_message_buffer;
1799 net_message.maxsize = sizeof(net_message_buffer);
1800
1801 M_Init ();
1802
1803 SCR_Init ();
1804 cls.disable_screen = true; // don't draw yet
1805
1806 CDAudio_Init ();
1807 CL_InitLocal ();
1808 IN_Init ();
1809
1810 // Cbuf_AddText ("exec autoexec.cfg\n");
1811 FS_ExecAutoexec ();
1812 Cbuf_Execute ();
1813
1814 }
1815
1816
1817 /*
1818 ===============
1819 CL_Shutdown
1820
1821 FIXME: this is a callback from Sys_Quit and Com_Error. It would be better
1822 to run quit through here before the final handoff to the sys code.
1823 ===============
1824 */
1825 void CL_Shutdown(void)
1826 {
1827 static qboolean isdown = false;
1828
1829 if (isdown)
1830 {
1831 printf ("recursive shutdown\n");
1832 return;
1833 }
1834 isdown = true;
1835
1836 CL_WriteConfiguration ();
1837
1838 CDAudio_Shutdown ();
1839 S_Shutdown();
1840 IN_Shutdown ();
1841 VID_Shutdown();
1842 }
1843
1844
1845