|
|
1.1 root 1: // cl_main.c -- client main loop
2: #include "client.h"
3: cvar_t *freelook;
4:
5: cvar_t *adr0;
6: cvar_t *adr1;
7: cvar_t *adr2;
8: cvar_t *adr3;
9: cvar_t *adr4;
10: cvar_t *adr5;
11: cvar_t *adr6;
12: cvar_t *adr7;
13: cvar_t *adr8;
14:
15: cvar_t *cl_stereo_separation;
16: cvar_t *cl_stereo;
17: cvar_t *rcon_client_password;
18: cvar_t *rcon_address;
19:
20: cvar_t *cl_noskins;
21: cvar_t *cl_autoskins;
22: cvar_t *cl_footsteps;
23: cvar_t *cl_timeout;
24: cvar_t *cl_predict;
25: cvar_t *cl_minfps;
26: cvar_t *cl_maxfps;
27: cvar_t *cl_gun;
28: cvar_t *cl_add_particles;
29: cvar_t *cl_add_lights;
30: cvar_t *cl_add_entities;
31: cvar_t *cl_add_blend;
32: cvar_t *cl_shownet;
33: cvar_t *cl_showmiss;
34: cvar_t *cl_showclamp;
35: cvar_t *cl_paused;
36: cvar_t *cl_timedemo;
37: cvar_t *lookspring;
38: cvar_t *lookstrafe;
39: cvar_t *sensitivity;
40: cvar_t *m_pitch;
41: cvar_t *m_yaw;
42: cvar_t *m_forward;
43: cvar_t *m_side;
44: cvar_t *cl_lightlevel;
45: //
46: // userinfo
47: //
48: cvar_t *info_password;
49: cvar_t *name;
50: cvar_t *skin;
51: cvar_t *rate;
52: cvar_t *fov;
53: cvar_t *msg;
54: cvar_t *hand;
55: client_static_t cls;
56: client_state_t cl;
57: centity_t cl_entities[MAX_EDICTS];
58:
59: entity_state_t cl_parse_entities[MAX_PARSE_ENTITIES];
60:
61: //======================================================================
62:
63:
64: /*
65: ====================
66: CL_WriteDemoMessage
67:
68: Dumps the current net message, prefixed by the length
69: ====================
70: */
71: void CL_WriteDemoMessage (void)
72: {
73: int len, swlen;
74:
75: // the first eight bytes are just packet sequencing stuff
76: len = net_message.cursize-8;
77: swlen = LittleLong(len);
78: fwrite (&swlen, 4, 1, cls.demofile);
79: fwrite (net_message.data+8, len, 1, cls.demofile);
80: }
81:
82:
83: /*
84: ====================
85: CL_Stop_f
86:
87: stop recording a demo
88: ====================
89: */
90: void CL_Stop_f (void)
91: {
92: int len;
93:
94: if (!cls.demorecording)
95: {
96: Com_Printf ("Not recording a demo.\n");
97: return;
98: }
99:
100: // finish up
101: len = -1;
102: fwrite (&len, 4, 1, cls.demofile);
103: fclose (cls.demofile);
104: cls.demofile = NULL;
105: cls.demorecording = false;
106: Com_Printf ("Stopped demo.\n");
107: }
108:
109: /*
110: ====================
111: CL_Record_f
112:
113: record <demoname>
114:
115: Begins recording a demo from the current position
116: ====================
117: */
118: void CL_Record_f (void)
119: {
120: char name[MAX_OSPATH];
121: char buf_data[MAX_MSGLEN];
122: sizebuf_t buf;
123: int i;
124: int len;
125: entity_state_t *ent;
126: entity_state_t nullstate;
127:
128: if (Cmd_Argc() != 2)
129: {
130: Com_Printf ("record <demoname>\n");
131: return;
132: }
133:
134: if (cls.demorecording)
135: {
136: Com_Printf ("Already recording.\n");
137: return;
138: }
139:
140: if (cls.state != ca_active)
141: {
142: Com_Printf ("You must be in a level to record.\n");
143: return;
144: }
145:
146: //
147: // open the demo file
148: //
149: Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
150:
151: Com_Printf ("recording to %s.\n", name);
152: FS_CreatePath (name);
153: cls.demofile = fopen (name, "wb");
154: if (!cls.demofile)
155: {
156: Com_Printf ("ERROR: couldn't open.\n");
157: return;
158: }
159: cls.demorecording = true;
160:
161: // don't start saving messages until a non-delta compressed message is received
162: cls.demowaiting = true;
163:
164: //
165: // write out messages to hold the startup information
166: //
167: SZ_Init (&buf, buf_data, sizeof(buf_data));
168:
169: // send the serverdata
170: MSG_WriteByte (&buf, svc_serverdata);
171: MSG_WriteLong (&buf, PROTOCOL_VERSION);
172: MSG_WriteLong (&buf, 0x10000 + cl.servercount);
173: MSG_WriteByte (&buf, 1); // demos are always attract loops
174: MSG_WriteString (&buf, cl.gamedir);
175: MSG_WriteShort (&buf, cl.playernum);
176:
177: MSG_WriteString (&buf, cl.configstrings[CS_NAME]);
178:
179: // configstrings
180: for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
181: {
182: if (cl.configstrings[i][0])
183: {
184: if (buf.cursize + strlen (cl.configstrings[i]) + 32 > buf.maxsize)
185: { // write it out
186: len = LittleLong (buf.cursize);
187: fwrite (&len, 4, 1, cls.demofile);
188: fwrite (buf.data, buf.cursize, 1, cls.demofile);
189: buf.cursize = 0;
190: }
191:
192: MSG_WriteByte (&buf, svc_configstring);
193: MSG_WriteShort (&buf, i);
194: MSG_WriteString (&buf, cl.configstrings[i]);
195: }
196:
197: }
198:
199: // baselines
200: memset (&nullstate, 0, sizeof(nullstate));
201: for (i=0; i<MAX_EDICTS ; i++)
202: {
203: ent = &cl_entities[i].baseline;
204: if (!ent->modelindex)
205: continue;
206:
207: if (buf.cursize + 64 > buf.maxsize)
208: { // write it out
209: len = LittleLong (buf.cursize);
210: fwrite (&len, 4, 1, cls.demofile);
211: fwrite (buf.data, buf.cursize, 1, cls.demofile);
212: buf.cursize = 0;
213: }
214:
215: MSG_WriteByte (&buf, svc_spawnbaseline);
216: MSG_WriteDeltaEntity (&nullstate, &cl_entities[i].baseline, &buf, true);
217: }
218:
219: MSG_WriteByte (&buf, svc_stufftext);
220: MSG_WriteString (&buf, "precache\n");
221:
222: // write it to the demo file
223:
224: len = LittleLong (buf.cursize);
225: fwrite (&len, 4, 1, cls.demofile);
226: fwrite (buf.data, buf.cursize, 1, cls.demofile);
227:
228: // the rest of the demo file will be individual frames
229: }
230:
231: //======================================================================
232: /*
233: ===================
234: Cmd_ForwardToServer
235: adds the current command line as a clc_stringcmd to the client message.
236: things like godmode, noclip, etc, are commands directed to the server,
237: so when they are typed in at the console, they will need to be forwarded.
238: ===================
239: */
240: void Cmd_ForwardToServer (void)
241: {
242: char *cmd;
243:
244: cmd = Cmd_Argv(0);
245: if (cls.state <= ca_connected || *cmd == '-' || *cmd == '+')
246: {
247: Com_Printf ("Unknown command \"%s\"\n", cmd);
248: return;
249: }
250:
251: MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
252: SZ_Print (&cls.netchan.message, cmd);
253: if (Cmd_Argc() > 1)
254: {
255: SZ_Print (&cls.netchan.message, " ");
256: SZ_Print (&cls.netchan.message, Cmd_Args());
257: }
258: }
259: void CL_Setenv_f( void )
260: {
261: int argc = Cmd_Argc();
262: if ( argc > 2 )
263: {
264: char buffer[1000];
265: int i;
266: strcpy( buffer, Cmd_Argv(1) );
267: strcat( buffer, "=" );
268: for ( i = 2; i < argc; i++ )
269: {
270: strcat( buffer, Cmd_Argv( i ) );
271: strcat( buffer, " " );
272: }
273: putenv( buffer );
274: }
275: else if ( argc == 2 )
276: {
277: char *env = getenv( Cmd_Argv(1) );
278:
279: if ( env )
280: {
281: Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
282: }
283: else
284: {
285: Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
286: }
287: }
288: }
289: /*
290: ==================
291: CL_ForwardToServer_f
292: ==================
293: */
294: void CL_ForwardToServer_f (void)
295: {
296: if (cls.state != ca_connected && cls.state != ca_active)
297: {
298: Com_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
299: return;
300: }
301:
302: // don't forward the first argument
303: if (Cmd_Argc() > 1)
304: {
305: MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
306: SZ_Print (&cls.netchan.message, Cmd_Args());
307: }
308: }
309: /*
310: ==================
311: CL_Pause_f
312: ==================
313: */
314: void CL_Pause_f (void)
315: {
316: // never pause in multiplayer
317: if (Cvar_VariableValue ("maxclients") > 1 || !Com_ServerState ())
318: {
319: Cvar_SetValue ("paused", 0);
320: return;
321: }
322:
323: Cvar_SetValue ("paused", !cl_paused->value);
324: }
325: /*
326: ==================
327: CL_Quit_f
328: ==================
329: */
330: void CL_Quit_f (void)
331: {
332: CL_Disconnect ();
333: Com_Quit ();
334: }
335: /*
336: ================
337: CL_Drop
338: Called after an ERR_DROP was thrown
339: ================
340: */
341: void CL_Drop (void)
342: {
343: if (cls.state == ca_uninitialized)
344: return;
345: if (cls.state == ca_disconnected)
346: return;
347: CL_Disconnect ();
348: // drop loading plaque unless this is the initial game start
349: if (cls.disable_servercount != -1)
350: SCR_EndLoadingPlaque (); // get rid of loading plaque
351: }
352: /*
353: =======================
354: CL_SendConnectPacket
355:
356: We have gotten a challenge from the server, so try and
357: connect.
358: ======================
359: */
360: void CL_SendConnectPacket (void)
361: {
362: netadr_t adr;
363: int port;
364:
365: if (!NET_StringToAdr (cls.servername, &adr))
366: {
367: Com_Printf ("Bad server address\n");
368: cls.connect_time = 0;
369: return;
370: }
371: if (adr.port == 0)
372: adr.port = BigShort (PORT_SERVER);
373:
374: port = Cvar_VariableValue ("qport");
375: userinfo_modified = false;
376:
377: Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n",
378: PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() );
379: }
380:
381: /*
382: =================
383: CL_CheckForResend
384: Resend a connect message if the last one has timed out
385: =================
386: */
387: void CL_CheckForResend (void)
388: {
389: netadr_t adr;
390:
391: // if the local server is running and we aren't
392: // then connect
393: if (cls.state == ca_disconnected && Com_ServerState() )
394: {
395: cls.state = ca_connecting;
396: strncpy (cls.servername, "localhost", sizeof(cls.servername)-1);
397: // we don't need a challenge on the localhost
398: CL_SendConnectPacket ();
399: return;
400: // cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
401: }
402: // resend if we haven't gotten a reply yet
403: if (cls.state != ca_connecting)
404: return;
405: if (cls.realtime - cls.connect_time < 3000)
406: return;
407:
408: if (!NET_StringToAdr (cls.servername, &adr))
409: {
410: Com_Printf ("Bad server address\n");
411: cls.state = ca_disconnected;
412: return;
413: }
414: if (adr.port == 0)
415: adr.port = BigShort (PORT_SERVER);
416:
417: cls.connect_time = cls.realtime; // for retransmit requests
418:
419: Com_Printf ("Connecting to %s...\n", cls.servername);
420:
421: Netchan_OutOfBandPrint (NS_CLIENT, adr, "getchallenge\n");
422: }
423: /*
424: ================
425: CL_Connect_f
426: ================
427: */
428: void CL_Connect_f (void)
429: {
430: char *server;
431: if (Cmd_Argc() != 2)
432: {
433: Com_Printf ("usage: connect <server>\n");
434: return;
435: }
436:
437: if (Com_ServerState ())
438: { // if running a local server, kill it and reissue
439: SV_Shutdown (va("Server quit\n", msg), false);
440: }
441: else
442: {
443: CL_Disconnect ();
444: }
445: server = Cmd_Argv (1);
446: NET_Config (true); // allow remote
447: CL_Disconnect ();
448: cls.state = ca_connecting;
449: strncpy (cls.servername, server, sizeof(cls.servername)-1);
450: cls.connect_time = -99999; // CL_CheckForResend() will fire immediately
451: }
452: /*
453: =====================
454: CL_Rcon_f
455: Send the rest of the command line over as
456: an unconnected command.
457: =====================
458: */
459: void CL_Rcon_f (void)
460: {
461: char message[1024];
462: int i;
463: netadr_t to;
464: if (!rcon_client_password->string)
465: {
466: Com_Printf ("You must set 'rcon_password' before\n"
467: "issuing an rcon command.\n");
468: return;
469: }
470: message[0] = (char)255;
471: message[1] = (char)255;
472: message[2] = (char)255;
473: message[3] = (char)255;
474: message[4] = 0;
475:
476: NET_Config (true); // allow remote
477: strcat (message, "rcon ");
478: strcat (message, rcon_client_password->string);
479: strcat (message, " ");
480: for (i=1 ; i<Cmd_Argc() ; i++)
481: {
482: strcat (message, Cmd_Argv(i));
483: strcat (message, " ");
484: }
485: if (cls.state >= ca_connected)
486: to = cls.netchan.remote_address;
487: else
488: {
489: if (!strlen(rcon_address->string))
490: {
491: Com_Printf ("You must either be connected,\n"
492: "or set the 'rcon_address' cvar\n"
493: "to issue rcon commands\n");
494: return;
495: }
496: NET_StringToAdr (rcon_address->string, &to);
497: if (to.port == 0)
498: to.port = BigShort (PORT_SERVER);
499: }
500:
501: NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
502: }
503: /*
504: =====================
505: CL_ClearState
506: =====================
507: */
508: void CL_ClearState (void)
509: {
510: S_StopAllSounds ();
511: CL_ClearEffects ();
512: CL_ClearTEnts ();
513: // wipe the entire cl structure
514: memset (&cl, 0, sizeof(cl));
515: memset (&cl_entities, 0, sizeof(cl_entities));
516: SZ_Clear (&cls.netchan.message);
517: }
518: /*
519: =====================
520: CL_Disconnect
521: Goes from a connected state to full screen console state
522: Sends a disconnect message to the server
523: This is also called on Com_Error, so it shouldn't cause any errors
524: =====================
525: */
526: void CL_Disconnect (void)
527: {
528: byte final[32];
529: if (cls.state == ca_disconnected)
530: return;
531: if (cl_timedemo && cl_timedemo->value)
532: {
533: int time;
534:
535: time = Sys_Milliseconds () - cl.timedemo_start;
536: if (time > 0)
537: Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames,
538: time/1000.0, cl.timedemo_frames*1000.0 / time);
539: }
540: VectorClear (cl.refdef.blend);
541: re.CinematicSetPalette(NULL);
542:
543: M_ForceMenuOff ();
544: cls.connect_time = 0;
545: SCR_StopCinematic ();
546: if (cls.demorecording)
547: CL_Stop_f ();
548: // send a disconnect message to the server
549: final[0] = clc_stringcmd;
550: strcpy ((char *)final+1, "disconnect");
551: Netchan_Transmit (&cls.netchan, strlen(final), final);
552: Netchan_Transmit (&cls.netchan, strlen(final), final);
553: Netchan_Transmit (&cls.netchan, strlen(final), final);
554: CL_ClearState ();
555: cls.state = ca_disconnected;
556: }
557: void CL_Disconnect_f (void)
558: {
559: Com_Error (ERR_DROP, "Disconnected from server");
560: }
561: /*
562: ====================
563: CL_Packet_f
564: packet <destination> <contents>
565: Contents allows \n escape character
566: ====================
567: */
568: void CL_Packet_f (void)
569: {
570: char send[2048];
571: int i, l;
572: char *in, *out;
573: netadr_t adr;
574: if (Cmd_Argc() != 3)
575: {
576: Com_Printf ("packet <destination> <contents>\n");
577: return;
578: }
579: NET_Config (true); // allow remote
580: if (!NET_StringToAdr (Cmd_Argv(1), &adr))
581: {
582: Com_Printf ("Bad address\n");
583: return;
584: }
585: if (!adr.port)
586: adr.port = BigShort (PORT_SERVER);
587: in = Cmd_Argv(2);
588: out = send+4;
589: send[0] = send[1] = send[2] = send[3] = (char)0xff;
590: l = strlen (in);
591: for (i=0 ; i<l ; i++)
592: {
593: if (in[i] == '\\' && in[i+1] == 'n')
594: {
595: *out++ = '\n';
596: i++;
597: }
598: else
599: *out++ = in[i];
600: }
601: *out = 0;
602: NET_SendPacket (NS_CLIENT, out-send, send, adr);
603: }
604: /*
605: =================
606: CL_Changing_f
607: Just sent as a hint to the client that they should
608: drop to full console
609: =================
610: */
611: void CL_Changing_f (void)
612: {
613: SCR_BeginLoadingPlaque ();
614: cls.state = ca_connected; // not active anymore, but not disconnected
615: Com_Printf ("\nChanging map...\n");
616: }
617: /*
618: =================
619: CL_Reconnect_f
620: The server is changing levels
621: =================
622: */
623: void CL_Reconnect_f (void)
624: {
625: S_StopAllSounds ();
626: Com_Printf ("reconnecting...\n");
627: cls.state = ca_connected;
628: MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
629: MSG_WriteString (&cls.netchan.message, "new");
630: }
631: /*
632: =================
633: CL_ParseStatusMessage
634: Handle a reply from a ping
635: =================
636: */
637: void CL_ParseStatusMessage (void)
638: {
639: char *s;
640:
641: s = MSG_ReadString(&net_message);
642: Com_Printf ("%s\n", s);
643: M_AddToServerList (net_from, s);
644: }
645: /*
646: =================
647: CL_PingServers_f
648: =================
649: */
650: void CL_PingServers_f (void)
651: {
652: int i;
653: netadr_t adr;
654: char name[32];
655: char *adrstring;
656: cvar_t *noudp;
657: cvar_t *noipx;
658: NET_Config (true); // allow remote
659: // send a broadcast packet
660: Com_Printf ("pinging broadcast...\n");
661:
662: noudp = Cvar_Get ("noudp", "0", CVAR_NOSET);
663: if (!noudp->value)
664: {
665: adr.type = NA_BROADCAST;
666: adr.port = BigShort(PORT_SERVER);
667: Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
668: }
669:
670: noipx = Cvar_Get ("noipx", "0", CVAR_NOSET);
671: if (!noipx->value)
672: {
673: adr.type = NA_BROADCAST_IPX;
674: adr.port = BigShort(PORT_SERVER);
675: Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
676: }
677: // send a packet to each address book entry
678: for (i=0 ; i<16 ; i++)
679: {
680: Com_sprintf (name, sizeof(name), "adr%i", i);
681: adrstring = Cvar_VariableString (name);
682: if (!adrstring || !adrstring[0])
683: continue;
684: Com_Printf ("pinging %s...\n", adrstring);
685: if (!NET_StringToAdr (adrstring, &adr))
686: {
687: Com_Printf ("Bad address: %s\n", adrstring);
688: continue;
689: }
690: if (!adr.port)
691: adr.port = BigShort(PORT_SERVER);
692: Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
693: }
694: }
695:
696: /*
697: =================
698: CL_Skins_f
699:
700: Load or download any custom player skins and models
701: =================
702: */
703: void CL_Skins_f (void)
704: {
705: int i;
706:
707: for (i=0 ; i<MAX_CLIENTS ; i++)
708: {
709: if (!cl.configstrings[CS_PLAYERSKINS+i][0])
710: continue;
711: Com_Printf ("client %i: %s\n", i, cl.configstrings[CS_PLAYERSKINS+i]);
712: SCR_UpdateScreen ();
713: Sys_SendKeyEvents (); // pump message loop
714: CL_ParseClientinfo (i);
715: }
716: }
717:
718: /*
719: =================
720: CL_ConnectionlessPacket
721: Responses to broadcasts, etc
722: =================
723: */
724: void CL_ConnectionlessPacket (void)
725: {
726: char *s;
727: char *c;
728:
729: MSG_BeginReading (&net_message);
730: MSG_ReadLong (&net_message); // skip the -1
731: s = MSG_ReadStringLine (&net_message);
732: Cmd_TokenizeString (s, false);
733: c = Cmd_Argv(0);
734: Com_Printf ("%s: %s\n", NET_AdrToString (net_from), c);
735: // server connection
736: if (!strcmp(c, "client_connect"))
737: {
738: if (cls.state == ca_connected)
739: {
740: Com_Printf ("Dup connect received. Ignored.\n");
741: return;
742: }
743: Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, cls.quakePort);
744: MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
745: MSG_WriteString (&cls.netchan.message, "new");
746: cls.state = ca_connected;
747: return;
748: }
749: // server responding to a status broadcast
750: if (!strcmp(c, "info"))
751: {
752: CL_ParseStatusMessage ();
753: return;
754: }
755: // remote command from gui front end
756: if (!strcmp(c, "cmd"))
757: {
758: if (!NET_IsLocalAddress(net_from))
759: {
760: Com_Printf ("Command packet from remote host. Ignored.\n");
761: return;
762: }
763: Sys_AppActivate ();
764: s = MSG_ReadString (&net_message);
765: Cbuf_AddText (s);
766: Cbuf_AddText ("\n");
767: return;
768: }
769: // print command from somewhere
770: if (!strcmp(c, "print"))
771: {
772: s = MSG_ReadString (&net_message);
773: Com_Printf (s);
774: return;
775: }
776: // ping from somewhere
777: if (!strcmp(c, "ping"))
778: {
779: Netchan_OutOfBandPrint (NS_CLIENT, net_from, "ack");
780: return;
781: }
782:
783: // challenge from the server we are connecting to
784: if (!strcmp(c, "challenge"))
785: {
786: cls.challenge = atoi(Cmd_Argv(1));
787: CL_SendConnectPacket ();
788: return;
789: }
790:
791: // echo request from server
792: if (!strcmp(c, "echo"))
793: {
794: Netchan_OutOfBandPrint (NS_CLIENT, net_from, "%s", Cmd_Argv(1) );
795: return;
796: }
797:
798: Com_Printf ("Unknown command.\n");
799: }
800: /*
801: =================
802: CL_DumpPackets
803:
804: A vain attempt to help bad TCP stacks that cause problems
805: when they overflow
806: =================
807: */
808: void CL_DumpPackets (void)
809: {
810: while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
811: {
812: Com_Printf ("dumnping a packet\n");
813: }
814: }
815: /*
816: =================
817: CL_ReadPackets
818: =================
819: */
820: void CL_ReadPackets (void)
821: {
822: while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
823: {
824: // Com_Printf ("packet\n");
825: //
826: // remote command packet
827: //
828: if (*(int *)net_message.data == -1)
829: {
830: CL_ConnectionlessPacket ();
831: continue;
832: }
833: if (cls.state == ca_disconnected || cls.state == ca_connecting)
834: continue; // dump it if not connected
835: if (net_message.cursize < 8)
836: {
837: Com_Printf ("%s: Runt packet\n",NET_AdrToString(net_from));
838: continue;
839: }
840: //
841: // packet from server
842: //
843: if (!NET_CompareAdr (net_from, cls.netchan.remote_address))
844: {
845: Com_DPrintf ("%s:sequenced packet without connection\n"
846: ,NET_AdrToString(net_from));
847: continue;
848: }
849: if (!Netchan_Process(&cls.netchan, &net_message))
850: continue; // wasn't accepted for some reason
851: CL_ParseServerMessage ();
852: }
853: //
854: // check timeout
855: //
856: if (cls.state >= ca_connected
857: && cls.realtime - cls.netchan.last_received > cl_timeout->value*1000)
858: {
859: if (++cl.timeoutcount > 5) // timeoutcount saves debugger
860: {
861: Com_Printf ("\nServer connection timed out.\n");
862: CL_Disconnect ();
863: return;
864: }
865: }
866: else
867: cl.timeoutcount = 0;
868:
869: }
870: //=============================================================================
871: /*
872: ==============
873: CL_Userinfo_f
874: ==============
875: */
876: void CL_Userinfo_f (void)
877: {
878: Com_Printf ("User info settings:\n");
879: Info_Print (Cvar_Userinfo());
880: }
881: /*
882: =================
883: CL_Snd_Restart_f
884: Restart the sound subsystem so it can pick up
885: new parameters and flush all sounds
886: =================
887: */
888: void CL_Snd_Restart_f (void)
889: {
890: S_Shutdown ();
891: S_Init ();
892: CL_RegisterSounds ();
893: }
894: /*
895: =================
896: CL_Precache_f
897: The server will send this command right
898: before allowing the client into the server
899: =================
900: */
901: void CL_Precache_f (void)
902: {
903: unsigned map_checksum; // for detecting cheater maps
904:
905: CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
906:
907: if (map_checksum != atoi(cl.configstrings[CS_MAPCHECKSUM]))
908: Com_Error (ERR_DROP, "Local map version differs from server: %i != '%s'\n",
909: map_checksum, cl.configstrings[CS_MAPCHECKSUM]);
910: CL_RegisterSounds ();
911: CL_PrepRefresh ();
912: }
913: /*
914: =================
915: CL_InitLocal
916: =================
917: */
918: void CL_InitLocal (void)
919: {
920: cls.state = ca_disconnected;
921: cls.realtime = Sys_Milliseconds ();
922: CL_InitInput ();
923:
924: adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE );
925: adr1 = Cvar_Get( "adr1", "", CVAR_ARCHIVE );
926: adr2 = Cvar_Get( "adr2", "", CVAR_ARCHIVE );
927: adr3 = Cvar_Get( "adr3", "", CVAR_ARCHIVE );
928: adr4 = Cvar_Get( "adr4", "", CVAR_ARCHIVE );
929: adr5 = Cvar_Get( "adr5", "", CVAR_ARCHIVE );
930: adr6 = Cvar_Get( "adr6", "", CVAR_ARCHIVE );
931: adr7 = Cvar_Get( "adr7", "", CVAR_ARCHIVE );
932: adr8 = Cvar_Get( "adr8", "", CVAR_ARCHIVE );
933: //
934: // register our variables
935: //
936: cl_stereo_separation = Cvar_Get( "cl_stereo_separation", "0.4", CVAR_ARCHIVE );
937: cl_stereo = Cvar_Get( "cl_stereo", "0", 0 );
938:
939: cl_add_blend = Cvar_Get ("cl_blend", "1", 0);
940: cl_add_lights = Cvar_Get ("cl_lights", "1", 0);
941: cl_add_particles = Cvar_Get ("cl_particles", "1", 0);
942: cl_add_entities = Cvar_Get ("cl_entities", "1", 0);
943: cl_gun = Cvar_Get ("cl_gun", "1", 0);
944: cl_footsteps = Cvar_Get ("cl_footsteps", "1", 0);
945: cl_noskins = Cvar_Get ("cl_noskins", "0", 0);
946: cl_autoskins = Cvar_Get ("cl_autoskins", "0", 0);
947: cl_predict = Cvar_Get ("cl_predict", "1", 0);
948: cl_minfps = Cvar_Get ("cl_minfps", "5", 0);
949: cl_maxfps = Cvar_Get ("cl_maxfps", "90", 0);
950: cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0);
951: cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0);
952: cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0);
953: cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", 0);
954: cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", 0);
955: cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
956: cl_run = Cvar_Get ("cl_run", "0", CVAR_ARCHIVE);
957: freelook = Cvar_Get( "freelook", "0", CVAR_ARCHIVE );
958: lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE);
959: lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE);
960: sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE);
961:
962: m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
963: m_yaw = Cvar_Get ("m_yaw", "0.022", 0);
964: m_forward = Cvar_Get ("m_forward", "1", 0);
965: m_side = Cvar_Get ("m_side", "1", 0);
966:
967: cl_shownet = Cvar_Get ("cl_shownet", "0", 0);
968: cl_showmiss = Cvar_Get ("cl_showmiss", "0", 0);
969: cl_showclamp = Cvar_Get ("showclamp", "0", 0);
970: cl_timeout = Cvar_Get ("cl_timeout", "120", 0);
971: cl_paused = Cvar_Get ("paused", "0", 0);
972: cl_timedemo = Cvar_Get ("timedemo", "0", 0);
973: rcon_client_password = Cvar_Get ("rcon_password", "", 0);
974: rcon_address = Cvar_Get ("rcon_address", "", 0);
975: cl_lightlevel = Cvar_Get ("r_lightlevel", "0", 0);
976: //
977: // userinfo
978: //
979: info_password = Cvar_Get ("password", "", CVAR_USERINFO);
980: name = Cvar_Get ("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE);
981: skin = Cvar_Get ("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE);
982: rate = Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE); // FIXME
983: msg = Cvar_Get ("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE);
984: hand = Cvar_Get ("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
985: fov = Cvar_Get ("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE);
986: //
987: // register our commands
988: //
989: Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
990: Cmd_AddCommand ("pause", CL_Pause_f);
991: Cmd_AddCommand ("pingservers", CL_PingServers_f);
992: Cmd_AddCommand ("skins", CL_Skins_f);
993: Cmd_AddCommand ("userinfo", CL_Userinfo_f);
994: Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
995: Cmd_AddCommand ("changing", CL_Changing_f);
996: Cmd_AddCommand ("disconnect", CL_Disconnect_f);
997: Cmd_AddCommand ("record", CL_Record_f);
998: Cmd_AddCommand ("stop", CL_Stop_f);
999: Cmd_AddCommand ("quit", CL_Quit_f);
1000: Cmd_AddCommand ("connect", CL_Connect_f);
1001: Cmd_AddCommand ("reconnect", CL_Reconnect_f);
1002: Cmd_AddCommand ("rcon", CL_Rcon_f);
1003:
1004: // Cmd_AddCommand ("packet", CL_Packet_f); // this is dangerous to leave in
1005: Cmd_AddCommand ("setenv", CL_Setenv_f );
1006: Cmd_AddCommand ("precache", CL_Precache_f);
1007: //
1008: // forward to server commands
1009: //
1010: // the only thing this does is allow command completion
1011: // to work -- all unknown commands are automatically
1012: // forwarded to the server
1013: Cmd_AddCommand ("wave", NULL);
1014: Cmd_AddCommand ("inven", NULL);
1015: Cmd_AddCommand ("kill", NULL);
1016: Cmd_AddCommand ("use", NULL);
1017: Cmd_AddCommand ("drop", NULL);
1018: Cmd_AddCommand ("say", NULL);
1019: Cmd_AddCommand ("say_team", NULL);
1020: Cmd_AddCommand ("info", NULL);
1021: Cmd_AddCommand ("prog", NULL);
1022: Cmd_AddCommand ("give", NULL);
1023: Cmd_AddCommand ("god", NULL);
1024: Cmd_AddCommand ("notarget", NULL);
1025: Cmd_AddCommand ("noclip", NULL);
1026: Cmd_AddCommand ("invuse", NULL);
1027: Cmd_AddCommand ("invprev", NULL);
1028: Cmd_AddCommand ("invnext", NULL);
1029: Cmd_AddCommand ("invdrop", NULL);
1030: Cmd_AddCommand ("weapnext", NULL);
1031: Cmd_AddCommand ("weapprev", NULL);
1032: }
1033: /*
1034: ===============
1035: CL_WriteConfiguration
1036: Writes key bindings and archived cvars to config.cfg
1037: ===============
1038: */
1039: void CL_WriteConfiguration (void)
1040: {
1041: FILE *f;
1042: char path[MAX_QPATH];
1043: if (cls.state == ca_uninitialized)
1044: return;
1045: Com_sprintf (path, sizeof(path),"%s/config.cfg",FS_Gamedir());
1046: f = fopen (path, "w");
1047: if (!f)
1048: {
1049: Com_Printf ("Couldn't write config.cfg.\n");
1050: return;
1051: }
1052: fprintf (f, "// generated by quake, do not modify\n");
1053: Key_WriteBindings (f);
1054: fclose (f);
1055: Cvar_WriteVariables (path);
1056: }
1057:
1058: /*
1059: ==================
1060: CL_FixCvarCheats
1061:
1062: ==================
1063: */
1064:
1065: typedef struct
1066: {
1067: char *name;
1068: char *value;
1069: cvar_t *var;
1070: } cheatvar_t;
1071:
1072: cheatvar_t cheatvars[] = {
1073: {"timescale", "1"},
1074: {"timedemo", "0"},
1075: {"r_drawworld", "1"},
1076: {"cl_testlights", "0"},
1077: {"r_fullbright", "0"},
1078: {"r_drawflat", "0"},
1079: {"paused", "0"},
1080: {"fixedtime", "0"},
1081: {NULL, NULL}
1082: };
1083:
1084: int numcheatvars;
1085:
1086: void CL_FixCvarCheats (void)
1087: {
1088: int i;
1089: cheatvar_t *var;
1090:
1091: if ( !strcmp(cl.configstrings[CS_MAXCLIENTS], "1")
1092: || !cl.configstrings[CS_MAXCLIENTS][0] )
1093: return; // single player can cheat
1094:
1095: // find all the cvars if we haven't done it yet
1096: if (!numcheatvars)
1097: {
1098: while (cheatvars[numcheatvars].name)
1099: {
1100: cheatvars[numcheatvars].var = Cvar_Get (cheatvars[numcheatvars].name,
1101: cheatvars[numcheatvars].value, 0);
1102: numcheatvars++;
1103: }
1104: }
1105:
1106: // make sure they are all set to the proper values
1107: for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
1108: {
1109: if ( strcmp (var->var->string, var->value) )
1110: {
1111: Cvar_Set (var->name, var->value);
1112: }
1113: }
1114: }
1115: //============================================================================
1116:
1117: /*
1118: ==================
1119: CL_SendCommand
1120:
1121: ==================
1122: */
1123: void CL_SendCommand (void)
1124: {
1125: // get new key events
1126: Sys_SendKeyEvents ();
1127:
1128: // allow mice or other external controllers to add commands
1129: IN_Commands ();
1130:
1131: // process console commands
1132: Cbuf_Execute ();
1133:
1134: // fix any cheating cvars
1135: CL_FixCvarCheats ();
1136:
1137: // send intentions now
1138: CL_SendCmd ();
1139:
1140: // resend a connection request if necessary
1141: CL_CheckForResend ();
1142: }
1143: /*
1144: ==================
1145: CL_Frame
1146: ==================
1147: */
1148: void CL_Frame (int msec)
1149: {
1150: static int extratime;
1151: static int lasttimecalled;
1152: if (dedicated->value)
1153: return;
1154: extratime += msec;
1155:
1156: if (!cl_timedemo->value)
1157: {
1158: if (cls.state == ca_connected && extratime < 100)
1159: return; // don't flood packets out while connecting
1160: if (extratime < 1000/cl_maxfps->value)
1161: return; // framerate is too high
1162: }
1163:
1164: // let the mouse activate or deactivate
1165: IN_Frame ();
1166: // decide the simulation time
1167: cls.frametime = extratime/1000.0;
1168: cl.time += extratime;
1169: cls.realtime = curtime;
1170: extratime = 0;
1171: if (cls.frametime > (1.0 / cl_minfps->value))
1172: cls.frametime = (1.0 / cl_minfps->value);
1173: // if in the debugger last frame, don't timeout
1174: if (msec > 5000)
1175: cls.netchan.last_received = Sys_Milliseconds ();
1176:
1177: // fetch results from server
1178: CL_ReadPackets ();
1179:
1180: // send a new command message to the server
1181: CL_SendCommand ();
1182:
1183: // predict all unacknowledged movements
1184: CL_PredictMovement ();
1185: // allow rendering DLL change
1186: VID_CheckChanges ();
1187: if (!cl.refresh_prepped && cls.state == ca_active)
1188: CL_PrepRefresh ();
1189:
1190: // update the screen
1191: if (host_speeds->value)
1192: time_before_ref = Sys_Milliseconds ();
1193: SCR_UpdateScreen ();
1194: if (host_speeds->value)
1195: time_after_ref = Sys_Milliseconds ();
1196: // update audio
1197: S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up);
1198:
1199: CDAudio_Update();
1200:
1201: // advance local effects for next frame
1202: CL_RunDLights ();
1203: CL_RunLightStyles ();
1204: SCR_RunCinematic ();
1205: SCR_RunConsole ();
1206:
1207: cls.framecount++;
1208:
1209: if ( log_stats->value )
1210: {
1211: if ( cls.state == ca_active )
1212: {
1213: if ( !lasttimecalled )
1214: {
1215: lasttimecalled = Sys_Milliseconds();
1216: if ( log_stats_file )
1217: fprintf( log_stats_file, "0\n" );
1218: }
1219: else
1220: {
1221: int now = Sys_Milliseconds();
1222:
1223: if ( log_stats_file )
1224: fprintf( log_stats_file, "%d\n", now - lasttimecalled );
1225: lasttimecalled = now;
1226: }
1227: }
1228: }
1229: }
1230: //============================================================================
1231: /*
1232: ====================
1233: CL_Init
1234: ====================
1235: */
1236: void CL_Init (void)
1237: {
1238: if (dedicated->value)
1239: return; // nothing running on the client
1240: // all archived variables will now be loaded
1241: Con_Init ();
1242: VID_Init ();
1243: S_Init (); // sound must be initialized after window is created
1244:
1245: V_Init ();
1246:
1247: net_message.data = net_message_buffer;
1248: net_message.maxsize = sizeof(net_message_buffer);
1249: M_Init ();
1250:
1251: SCR_Init ();
1252: cls.disable_screen = true; // don't draw yet
1253: CDAudio_Init ();
1254: CL_InitLocal ();
1255: IN_Init ();
1256: // Cbuf_AddText ("exec autoexec.cfg\n");
1257: FS_ExecAutoexec ();
1258: Cbuf_Execute ();
1259: }
1260: /*
1261: ===============
1262: CL_Shutdown
1263: FIXME: this is a callback from Sys_Quit and Com_Error. It would be better
1264: to run quit through here before the final handoff to the sys code.
1265: ===============
1266: */
1267: void CL_Shutdown(void)
1268: {
1269: static qboolean isdown = false;
1270:
1271: if (isdown)
1272: {
1273: printf ("recursive shutdown\n");
1274: return;
1275: }
1276: isdown = true;
1277: CL_WriteConfiguration ();
1278: CDAudio_Shutdown ();
1279: S_Shutdown();
1280: IN_Shutdown ();
1281: VID_Shutdown();
1282: }
1283:
1284:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.