cl_main.c (40005B)
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 1705 if (cls.frametime > (1.0 / cl_minfps->value)) 1706 cls.frametime = (1.0 / cl_minfps->value); 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 1789 S_Init (); 1790 VID_Init (); 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