|
|
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.