cl_main.c (77864B)
1 /* 2 =========================================================================== 3 Copyright (C) 1999-2005 Id Software, Inc. 4 5 This file is part of Quake III Arena source code. 6 7 Quake III Arena source code is free software; you can redistribute it 8 and/or modify it under the terms of the GNU General Public License as 9 published by the Free Software Foundation; either version 2 of the License, 10 or (at your option) any later version. 11 12 Quake III Arena source code is distributed in the hope that it will be 13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with Foobar; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 20 =========================================================================== 21 */ 22 // cl_main.c -- client main loop 23 24 #include "client.h" 25 #include <limits.h> 26 27 cvar_t *cl_nodelta; 28 cvar_t *cl_debugMove; 29 30 cvar_t *cl_noprint; 31 cvar_t *cl_motd; 32 33 cvar_t *rcon_client_password; 34 cvar_t *rconAddress; 35 36 cvar_t *cl_timeout; 37 cvar_t *cl_maxpackets; 38 cvar_t *cl_packetdup; 39 cvar_t *cl_timeNudge; 40 cvar_t *cl_showTimeDelta; 41 cvar_t *cl_freezeDemo; 42 43 cvar_t *cl_shownet; 44 cvar_t *cl_showSend; 45 cvar_t *cl_timedemo; 46 cvar_t *cl_avidemo; 47 cvar_t *cl_forceavidemo; 48 49 cvar_t *cl_freelook; 50 cvar_t *cl_sensitivity; 51 52 cvar_t *cl_mouseAccel; 53 cvar_t *cl_showMouseRate; 54 55 cvar_t *m_pitch; 56 cvar_t *m_yaw; 57 cvar_t *m_forward; 58 cvar_t *m_side; 59 cvar_t *m_filter; 60 61 cvar_t *cl_activeAction; 62 63 cvar_t *cl_motdString; 64 65 cvar_t *cl_allowDownload; 66 cvar_t *cl_conXOffset; 67 cvar_t *cl_inGameVideo; 68 69 cvar_t *cl_serverStatusResendTime; 70 cvar_t *cl_trn; 71 72 clientActive_t cl; 73 clientConnection_t clc; 74 clientStatic_t cls; 75 vm_t *cgvm; 76 77 // Structure containing functions exported from refresh DLL 78 refexport_t re; 79 80 ping_t cl_pinglist[MAX_PINGREQUESTS]; 81 82 typedef struct serverStatus_s 83 { 84 char string[BIG_INFO_STRING]; 85 netadr_t address; 86 int time, startTime; 87 qboolean pending; 88 qboolean print; 89 qboolean retrieved; 90 } serverStatus_t; 91 92 serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS]; 93 int serverStatusCount; 94 95 #if defined __USEA3D && defined __A3D_GEOM 96 void hA3Dg_ExportRenderGeom (refexport_t *incoming_re); 97 #endif 98 99 extern void SV_BotFrame( int time ); 100 void CL_CheckForResend( void ); 101 void CL_ShowIP_f(void); 102 void CL_ServerStatus_f(void); 103 void CL_ServerStatusResponse( netadr_t from, msg_t *msg ); 104 105 /* 106 =============== 107 CL_CDDialog 108 109 Called by Com_Error when a cd is needed 110 =============== 111 */ 112 void CL_CDDialog( void ) { 113 cls.cddialog = qtrue; // start it next frame 114 } 115 116 117 /* 118 ======================================================================= 119 120 CLIENT RELIABLE COMMAND COMMUNICATION 121 122 ======================================================================= 123 */ 124 125 /* 126 ====================== 127 CL_AddReliableCommand 128 129 The given command will be transmitted to the server, and is gauranteed to 130 not have future usercmd_t executed before it is executed 131 ====================== 132 */ 133 void CL_AddReliableCommand( const char *cmd ) { 134 int index; 135 136 // if we would be losing an old command that hasn't been acknowledged, 137 // we must drop the connection 138 if ( clc.reliableSequence - clc.reliableAcknowledge > MAX_RELIABLE_COMMANDS ) { 139 Com_Error( ERR_DROP, "Client command overflow" ); 140 } 141 clc.reliableSequence++; 142 index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); 143 Q_strncpyz( clc.reliableCommands[ index ], cmd, sizeof( clc.reliableCommands[ index ] ) ); 144 } 145 146 /* 147 ====================== 148 CL_ChangeReliableCommand 149 ====================== 150 */ 151 void CL_ChangeReliableCommand( void ) { 152 int r, index, l; 153 154 r = clc.reliableSequence - (random() * 5); 155 index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 ); 156 l = strlen(clc.reliableCommands[ index ]); 157 if ( l >= MAX_STRING_CHARS - 1 ) { 158 l = MAX_STRING_CHARS - 2; 159 } 160 clc.reliableCommands[ index ][ l ] = '\n'; 161 clc.reliableCommands[ index ][ l+1 ] = '\0'; 162 } 163 164 /* 165 ======================================================================= 166 167 CLIENT SIDE DEMO RECORDING 168 169 ======================================================================= 170 */ 171 172 /* 173 ==================== 174 CL_WriteDemoMessage 175 176 Dumps the current net message, prefixed by the length 177 ==================== 178 */ 179 void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) { 180 int len, swlen; 181 182 // write the packet sequence 183 len = clc.serverMessageSequence; 184 swlen = LittleLong( len ); 185 FS_Write (&swlen, 4, clc.demofile); 186 187 // skip the packet sequencing information 188 len = msg->cursize - headerBytes; 189 swlen = LittleLong(len); 190 FS_Write (&swlen, 4, clc.demofile); 191 FS_Write ( msg->data + headerBytes, len, clc.demofile ); 192 } 193 194 195 /* 196 ==================== 197 CL_StopRecording_f 198 199 stop recording a demo 200 ==================== 201 */ 202 void CL_StopRecord_f( void ) { 203 int len; 204 205 if ( !clc.demorecording ) { 206 Com_Printf ("Not recording a demo.\n"); 207 return; 208 } 209 210 // finish up 211 len = -1; 212 FS_Write (&len, 4, clc.demofile); 213 FS_Write (&len, 4, clc.demofile); 214 FS_FCloseFile (clc.demofile); 215 clc.demofile = 0; 216 clc.demorecording = qfalse; 217 clc.spDemoRecording = qfalse; 218 Com_Printf ("Stopped demo.\n"); 219 } 220 221 /* 222 ================== 223 CL_DemoFilename 224 ================== 225 */ 226 void CL_DemoFilename( int number, char *fileName ) { 227 int a,b,c,d; 228 229 if ( number < 0 || number > 9999 ) { 230 Com_sprintf( fileName, MAX_OSPATH, "demo9999.tga" ); 231 return; 232 } 233 234 a = number / 1000; 235 number -= a*1000; 236 b = number / 100; 237 number -= b*100; 238 c = number / 10; 239 number -= c*10; 240 d = number; 241 242 Com_sprintf( fileName, MAX_OSPATH, "demo%i%i%i%i" 243 , a, b, c, d ); 244 } 245 246 /* 247 ==================== 248 CL_Record_f 249 250 record <demoname> 251 252 Begins recording a demo from the current position 253 ==================== 254 */ 255 static char demoName[MAX_QPATH]; // compiler bug workaround 256 void CL_Record_f( void ) { 257 char name[MAX_OSPATH]; 258 byte bufData[MAX_MSGLEN]; 259 msg_t buf; 260 int i; 261 int len; 262 entityState_t *ent; 263 entityState_t nullstate; 264 char *s; 265 266 if ( Cmd_Argc() > 2 ) { 267 Com_Printf ("record <demoname>\n"); 268 return; 269 } 270 271 if ( clc.demorecording ) { 272 if (!clc.spDemoRecording) { 273 Com_Printf ("Already recording.\n"); 274 } 275 return; 276 } 277 278 if ( cls.state != CA_ACTIVE ) { 279 Com_Printf ("You must be in a level to record.\n"); 280 return; 281 } 282 283 // sync 0 doesn't prevent recording, so not forcing it off .. everyone does g_sync 1 ; record ; g_sync 0 .. 284 if ( !Cvar_VariableValue( "g_synchronousClients" ) ) { 285 Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n"); 286 } 287 288 if ( Cmd_Argc() == 2 ) { 289 s = Cmd_Argv(1); 290 Q_strncpyz( demoName, s, sizeof( demoName ) ); 291 Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION ); 292 } else { 293 int number; 294 295 // scan for a free demo name 296 for ( number = 0 ; number <= 9999 ; number++ ) { 297 CL_DemoFilename( number, demoName ); 298 Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION ); 299 300 len = FS_ReadFile( name, NULL ); 301 if ( len <= 0 ) { 302 break; // file doesn't exist 303 } 304 } 305 } 306 307 // open the demo file 308 309 Com_Printf ("recording to %s.\n", name); 310 clc.demofile = FS_FOpenFileWrite( name ); 311 if ( !clc.demofile ) { 312 Com_Printf ("ERROR: couldn't open.\n"); 313 return; 314 } 315 clc.demorecording = qtrue; 316 if (Cvar_VariableValue("ui_recordSPDemo")) { 317 clc.spDemoRecording = qtrue; 318 } else { 319 clc.spDemoRecording = qfalse; 320 } 321 322 323 Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) ); 324 325 // don't start saving messages until a non-delta compressed message is received 326 clc.demowaiting = qtrue; 327 328 // write out the gamestate message 329 MSG_Init (&buf, bufData, sizeof(bufData)); 330 MSG_Bitstream(&buf); 331 332 // NOTE, MRE: all server->client messages now acknowledge 333 MSG_WriteLong( &buf, clc.reliableSequence ); 334 335 MSG_WriteByte (&buf, svc_gamestate); 336 MSG_WriteLong (&buf, clc.serverCommandSequence ); 337 338 // configstrings 339 for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { 340 if ( !cl.gameState.stringOffsets[i] ) { 341 continue; 342 } 343 s = cl.gameState.stringData + cl.gameState.stringOffsets[i]; 344 MSG_WriteByte (&buf, svc_configstring); 345 MSG_WriteShort (&buf, i); 346 MSG_WriteBigString (&buf, s); 347 } 348 349 // baselines 350 Com_Memset (&nullstate, 0, sizeof(nullstate)); 351 for ( i = 0; i < MAX_GENTITIES ; i++ ) { 352 ent = &cl.entityBaselines[i]; 353 if ( !ent->number ) { 354 continue; 355 } 356 MSG_WriteByte (&buf, svc_baseline); 357 MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue ); 358 } 359 360 MSG_WriteByte( &buf, svc_EOF ); 361 362 // finished writing the gamestate stuff 363 364 // write the client num 365 MSG_WriteLong(&buf, clc.clientNum); 366 // write the checksum feed 367 MSG_WriteLong(&buf, clc.checksumFeed); 368 369 // finished writing the client packet 370 MSG_WriteByte( &buf, svc_EOF ); 371 372 // write it to the demo file 373 len = LittleLong( clc.serverMessageSequence - 1 ); 374 FS_Write (&len, 4, clc.demofile); 375 376 len = LittleLong (buf.cursize); 377 FS_Write (&len, 4, clc.demofile); 378 FS_Write (buf.data, buf.cursize, clc.demofile); 379 380 // the rest of the demo file will be copied from net messages 381 } 382 383 /* 384 ======================================================================= 385 386 CLIENT SIDE DEMO PLAYBACK 387 388 ======================================================================= 389 */ 390 391 /* 392 ================= 393 CL_DemoCompleted 394 ================= 395 */ 396 void CL_DemoCompleted( void ) { 397 if (cl_timedemo && cl_timedemo->integer) { 398 int time; 399 400 time = Sys_Milliseconds() - clc.timeDemoStart; 401 if ( time > 0 ) { 402 Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", clc.timeDemoFrames, 403 time/1000.0, clc.timeDemoFrames*1000.0 / time); 404 } 405 } 406 407 CL_Disconnect( qtrue ); 408 CL_NextDemo(); 409 } 410 411 /* 412 ================= 413 CL_ReadDemoMessage 414 ================= 415 */ 416 void CL_ReadDemoMessage( void ) { 417 int r; 418 msg_t buf; 419 byte bufData[ MAX_MSGLEN ]; 420 int s; 421 422 if ( !clc.demofile ) { 423 CL_DemoCompleted (); 424 return; 425 } 426 427 // get the sequence number 428 r = FS_Read( &s, 4, clc.demofile); 429 if ( r != 4 ) { 430 CL_DemoCompleted (); 431 return; 432 } 433 clc.serverMessageSequence = LittleLong( s ); 434 435 // init the message 436 MSG_Init( &buf, bufData, sizeof( bufData ) ); 437 438 // get the length 439 r = FS_Read (&buf.cursize, 4, clc.demofile); 440 if ( r != 4 ) { 441 CL_DemoCompleted (); 442 return; 443 } 444 buf.cursize = LittleLong( buf.cursize ); 445 if ( buf.cursize == -1 ) { 446 CL_DemoCompleted (); 447 return; 448 } 449 if ( buf.cursize > buf.maxsize ) { 450 Com_Error (ERR_DROP, "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN"); 451 } 452 r = FS_Read( buf.data, buf.cursize, clc.demofile ); 453 if ( r != buf.cursize ) { 454 Com_Printf( "Demo file was truncated.\n"); 455 CL_DemoCompleted (); 456 return; 457 } 458 459 clc.lastPacketTime = cls.realtime; 460 buf.readcount = 0; 461 CL_ParseServerMessage( &buf ); 462 } 463 464 /* 465 ==================== 466 CL_WalkDemoExt 467 ==================== 468 */ 469 static void CL_WalkDemoExt(char *arg, char *name, int *demofile) 470 { 471 int i = 0; 472 *demofile = 0; 473 while(demo_protocols[i]) 474 { 475 Com_sprintf (name, MAX_OSPATH, "demos/%s.dm_%d", arg, demo_protocols[i]); 476 FS_FOpenFileRead( name, demofile, qtrue ); 477 if (*demofile) 478 { 479 Com_Printf("Demo file: %s\n", name); 480 break; 481 } 482 else 483 Com_Printf("Not found: %s\n", name); 484 i++; 485 } 486 } 487 488 /* 489 ==================== 490 CL_PlayDemo_f 491 492 demo <demoname> 493 494 ==================== 495 */ 496 void CL_PlayDemo_f( void ) { 497 char name[MAX_OSPATH]; 498 char *arg, *ext_test; 499 int protocol, i; 500 char retry[MAX_OSPATH]; 501 502 if (Cmd_Argc() != 2) { 503 Com_Printf ("playdemo <demoname>\n"); 504 return; 505 } 506 507 // make sure a local server is killed 508 Cvar_Set( "sv_killserver", "1" ); 509 510 CL_Disconnect( qtrue ); 511 512 // open the demo file 513 arg = Cmd_Argv(1); 514 515 // check for an extension .dm_?? (?? is protocol) 516 ext_test = arg + strlen(arg) - 6; 517 if ((strlen(arg) > 6) && (ext_test[0] == '.') && ((ext_test[1] == 'd') || (ext_test[1] == 'D')) && ((ext_test[2] == 'm') || (ext_test[2] == 'M')) && (ext_test[3] == '_')) 518 { 519 protocol = atoi(ext_test+4); 520 i=0; 521 while(demo_protocols[i]) 522 { 523 if (demo_protocols[i] == protocol) 524 break; 525 i++; 526 } 527 if (demo_protocols[i]) 528 { 529 Com_sprintf (name, sizeof(name), "demos/%s", arg); 530 FS_FOpenFileRead( name, &clc.demofile, qtrue ); 531 } else { 532 Com_Printf("Protocol %d not supported for demos\n", protocol); 533 Q_strncpyz(retry, arg, sizeof(retry)); 534 retry[strlen(retry)-6] = 0; 535 CL_WalkDemoExt( retry, name, &clc.demofile ); 536 } 537 } else { 538 CL_WalkDemoExt( arg, name, &clc.demofile ); 539 } 540 541 if (!clc.demofile) { 542 Com_Error( ERR_DROP, "couldn't open %s", name); 543 return; 544 } 545 Q_strncpyz( clc.demoName, Cmd_Argv(1), sizeof( clc.demoName ) ); 546 547 Con_Close(); 548 549 cls.state = CA_CONNECTED; 550 clc.demoplaying = qtrue; 551 Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) ); 552 553 // read demo messages until connected 554 while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) { 555 CL_ReadDemoMessage(); 556 } 557 // don't get the first snapshot this frame, to prevent the long 558 // time from the gamestate load from messing causing a time skip 559 clc.firstDemoFrameSkipped = qfalse; 560 } 561 562 563 /* 564 ==================== 565 CL_StartDemoLoop 566 567 Closing the main menu will restart the demo loop 568 ==================== 569 */ 570 void CL_StartDemoLoop( void ) { 571 // start the demo loop again 572 Cbuf_AddText ("d1\n"); 573 cls.keyCatchers = 0; 574 } 575 576 /* 577 ================== 578 CL_NextDemo 579 580 Called when a demo or cinematic finishes 581 If the "nextdemo" cvar is set, that command will be issued 582 ================== 583 */ 584 void CL_NextDemo( void ) { 585 char v[MAX_STRING_CHARS]; 586 587 Q_strncpyz( v, Cvar_VariableString ("nextdemo"), sizeof(v) ); 588 v[MAX_STRING_CHARS-1] = 0; 589 Com_DPrintf("CL_NextDemo: %s\n", v ); 590 if (!v[0]) { 591 return; 592 } 593 594 Cvar_Set ("nextdemo",""); 595 Cbuf_AddText (v); 596 Cbuf_AddText ("\n"); 597 Cbuf_Execute(); 598 } 599 600 601 //====================================================================== 602 603 /* 604 ===================== 605 CL_ShutdownAll 606 ===================== 607 */ 608 void CL_ShutdownAll(void) { 609 610 // clear sounds 611 S_DisableSounds(); 612 // shutdown CGame 613 CL_ShutdownCGame(); 614 // shutdown UI 615 CL_ShutdownUI(); 616 617 // shutdown the renderer 618 if ( re.Shutdown ) { 619 re.Shutdown( qfalse ); // don't destroy window or context 620 } 621 622 cls.uiStarted = qfalse; 623 cls.cgameStarted = qfalse; 624 cls.rendererStarted = qfalse; 625 cls.soundRegistered = qfalse; 626 } 627 628 /* 629 ================= 630 CL_FlushMemory 631 632 Called by CL_MapLoading, CL_Connect_f, CL_PlayDemo_f, and CL_ParseGamestate the only 633 ways a client gets into a game 634 Also called by Com_Error 635 ================= 636 */ 637 void CL_FlushMemory( void ) { 638 639 // shutdown all the client stuff 640 CL_ShutdownAll(); 641 642 // if not running a server clear the whole hunk 643 if ( !com_sv_running->integer ) { 644 // clear the whole hunk 645 Hunk_Clear(); 646 // clear collision map data 647 CM_ClearMap(); 648 } 649 else { 650 // clear all the client data on the hunk 651 Hunk_ClearToMark(); 652 } 653 654 CL_StartHunkUsers(); 655 } 656 657 /* 658 ===================== 659 CL_MapLoading 660 661 A local server is starting to load a map, so update the 662 screen to let the user know about it, then dump all client 663 memory on the hunk from cgame, ui, and renderer 664 ===================== 665 */ 666 void CL_MapLoading( void ) { 667 if ( !com_cl_running->integer ) { 668 return; 669 } 670 671 Con_Close(); 672 cls.keyCatchers = 0; 673 674 // if we are already connected to the local host, stay connected 675 if ( cls.state >= CA_CONNECTED && !Q_stricmp( cls.servername, "localhost" ) ) { 676 cls.state = CA_CONNECTED; // so the connect screen is drawn 677 Com_Memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) ); 678 Com_Memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) ); 679 Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) ); 680 clc.lastPacketSentTime = -9999; 681 SCR_UpdateScreen(); 682 } else { 683 // clear nextmap so the cinematic shutdown doesn't execute it 684 Cvar_Set( "nextmap", "" ); 685 CL_Disconnect( qtrue ); 686 Q_strncpyz( cls.servername, "localhost", sizeof(cls.servername) ); 687 cls.state = CA_CHALLENGING; // so the connect screen is drawn 688 cls.keyCatchers = 0; 689 SCR_UpdateScreen(); 690 clc.connectTime = -RETRANSMIT_TIMEOUT; 691 NET_StringToAdr( cls.servername, &clc.serverAddress); 692 // we don't need a challenge on the localhost 693 694 CL_CheckForResend(); 695 } 696 } 697 698 /* 699 ===================== 700 CL_ClearState 701 702 Called before parsing a gamestate 703 ===================== 704 */ 705 void CL_ClearState (void) { 706 707 // S_StopAllSounds(); 708 709 Com_Memset( &cl, 0, sizeof( cl ) ); 710 } 711 712 713 /* 714 ===================== 715 CL_Disconnect 716 717 Called when a connection, demo, or cinematic is being terminated. 718 Goes from a connected state to either a menu state or a console state 719 Sends a disconnect message to the server 720 This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors 721 ===================== 722 */ 723 void CL_Disconnect( qboolean showMainMenu ) { 724 if ( !com_cl_running || !com_cl_running->integer ) { 725 return; 726 } 727 728 // shutting down the client so enter full screen ui mode 729 Cvar_Set("r_uiFullScreen", "1"); 730 731 if ( clc.demorecording ) { 732 CL_StopRecord_f (); 733 } 734 735 if (clc.download) { 736 FS_FCloseFile( clc.download ); 737 clc.download = 0; 738 } 739 *clc.downloadTempName = *clc.downloadName = 0; 740 Cvar_Set( "cl_downloadName", "" ); 741 742 if ( clc.demofile ) { 743 FS_FCloseFile( clc.demofile ); 744 clc.demofile = 0; 745 } 746 747 if ( uivm && showMainMenu ) { 748 VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE ); 749 } 750 751 SCR_StopCinematic (); 752 S_ClearSoundBuffer(); 753 754 // send a disconnect message to the server 755 // send it a few times in case one is dropped 756 if ( cls.state >= CA_CONNECTED ) { 757 CL_AddReliableCommand( "disconnect" ); 758 CL_WritePacket(); 759 CL_WritePacket(); 760 CL_WritePacket(); 761 } 762 763 CL_ClearState (); 764 765 // wipe the client connection 766 Com_Memset( &clc, 0, sizeof( clc ) ); 767 768 cls.state = CA_DISCONNECTED; 769 770 // allow cheats locally 771 Cvar_Set( "sv_cheats", "1" ); 772 773 // not connected to a pure server anymore 774 cl_connectedToPureServer = qfalse; 775 } 776 777 778 /* 779 =================== 780 CL_ForwardCommandToServer 781 782 adds the current command line as a clientCommand 783 things like godmode, noclip, etc, are commands directed to the server, 784 so when they are typed in at the console, they will need to be forwarded. 785 =================== 786 */ 787 void CL_ForwardCommandToServer( const char *string ) { 788 char *cmd; 789 790 cmd = Cmd_Argv(0); 791 792 // ignore key up commands 793 if ( cmd[0] == '-' ) { 794 return; 795 } 796 797 if ( clc.demoplaying || cls.state < CA_CONNECTED || cmd[0] == '+' ) { 798 Com_Printf ("Unknown command \"%s\"\n", cmd); 799 return; 800 } 801 802 if ( Cmd_Argc() > 1 ) { 803 CL_AddReliableCommand( string ); 804 } else { 805 CL_AddReliableCommand( cmd ); 806 } 807 } 808 809 /* 810 =================== 811 CL_RequestMotd 812 813 =================== 814 */ 815 void CL_RequestMotd( void ) { 816 char info[MAX_INFO_STRING]; 817 818 if ( !cl_motd->integer ) { 819 return; 820 } 821 Com_Printf( "Resolving %s\n", UPDATE_SERVER_NAME ); 822 if ( !NET_StringToAdr( UPDATE_SERVER_NAME, &cls.updateServer ) ) { 823 Com_Printf( "Couldn't resolve address\n" ); 824 return; 825 } 826 cls.updateServer.port = BigShort( PORT_UPDATE ); 827 Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME, 828 cls.updateServer.ip[0], cls.updateServer.ip[1], 829 cls.updateServer.ip[2], cls.updateServer.ip[3], 830 BigShort( cls.updateServer.port ) ); 831 832 info[0] = 0; 833 // NOTE TTimo xoring against Com_Milliseconds, otherwise we may not have a true randomization 834 // only srand I could catch before here is tr_noise.c l:26 srand(1001) 835 // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=382 836 // NOTE: the Com_Milliseconds xoring only affects the lower 16-bit word, 837 // but I decided it was enough randomization 838 Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds()); 839 840 Info_SetValueForKey( info, "challenge", cls.updateChallenge ); 841 Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string ); 842 Info_SetValueForKey( info, "version", com_version->string ); 843 844 NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info ); 845 } 846 847 /* 848 =================== 849 CL_RequestAuthorization 850 851 Authorization server protocol 852 ----------------------------- 853 854 All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff). 855 856 Whenever the client tries to get a challenge from the server it wants to 857 connect to, it also blindly fires off a packet to the authorize server: 858 859 getKeyAuthorize <challenge> <cdkey> 860 861 cdkey may be "demo" 862 863 864 #OLD The authorize server returns a: 865 #OLD 866 #OLD keyAthorize <challenge> <accept | deny> 867 #OLD 868 #OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP 869 #OLD address in the last 15 minutes. 870 871 872 The server sends a: 873 874 getIpAuthorize <challenge> <ip> 875 876 The authorize server returns a: 877 878 ipAuthorize <challenge> <accept | deny | demo | unknown > 879 880 A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes. 881 If no response is received from the authorize server after two tries, the client will be let 882 in anyway. 883 =================== 884 */ 885 void CL_RequestAuthorization( void ) { 886 char nums[64]; 887 int i, j, l; 888 cvar_t *fs; 889 890 if ( !cls.authorizeServer.port ) { 891 Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME ); 892 if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer ) ) { 893 Com_Printf( "Couldn't resolve address\n" ); 894 return; 895 } 896 897 cls.authorizeServer.port = BigShort( PORT_AUTHORIZE ); 898 Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME, 899 cls.authorizeServer.ip[0], cls.authorizeServer.ip[1], 900 cls.authorizeServer.ip[2], cls.authorizeServer.ip[3], 901 BigShort( cls.authorizeServer.port ) ); 902 } 903 if ( cls.authorizeServer.type == NA_BAD ) { 904 return; 905 } 906 907 if ( Cvar_VariableValue( "fs_restrict" ) ) { 908 Q_strncpyz( nums, "demota", sizeof( nums ) ); 909 } else { 910 // only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces 911 j = 0; 912 l = strlen( cl_cdkey ); 913 if ( l > 32 ) { 914 l = 32; 915 } 916 for ( i = 0 ; i < l ; i++ ) { 917 if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' ) 918 || ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' ) 919 || ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' ) 920 ) { 921 nums[j] = cl_cdkey[i]; 922 j++; 923 } 924 } 925 nums[j] = 0; 926 } 927 928 fs = Cvar_Get ("cl_anonymous", "0", CVAR_INIT|CVAR_SYSTEMINFO ); 929 930 NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, va("getKeyAuthorize %i %s", fs->integer, nums) ); 931 } 932 933 /* 934 ====================================================================== 935 936 CONSOLE COMMANDS 937 938 ====================================================================== 939 */ 940 941 /* 942 ================== 943 CL_ForwardToServer_f 944 ================== 945 */ 946 void CL_ForwardToServer_f( void ) { 947 if ( cls.state != CA_ACTIVE || clc.demoplaying ) { 948 Com_Printf ("Not connected to a server.\n"); 949 return; 950 } 951 952 // don't forward the first argument 953 if ( Cmd_Argc() > 1 ) { 954 CL_AddReliableCommand( Cmd_Args() ); 955 } 956 } 957 958 /* 959 ================== 960 CL_Setenv_f 961 962 Mostly for controlling voodoo environment variables 963 ================== 964 */ 965 void CL_Setenv_f( void ) { 966 int argc = Cmd_Argc(); 967 968 if ( argc > 2 ) { 969 char buffer[1024]; 970 int i; 971 972 strcpy( buffer, Cmd_Argv(1) ); 973 strcat( buffer, "=" ); 974 975 for ( i = 2; i < argc; i++ ) { 976 strcat( buffer, Cmd_Argv( i ) ); 977 strcat( buffer, " " ); 978 } 979 980 putenv( buffer ); 981 } else if ( argc == 2 ) { 982 char *env = getenv( Cmd_Argv(1) ); 983 984 if ( env ) { 985 Com_Printf( "%s=%s\n", Cmd_Argv(1), env ); 986 } else { 987 Com_Printf( "%s undefined\n", Cmd_Argv(1), env ); 988 } 989 } 990 } 991 992 993 /* 994 ================== 995 CL_Disconnect_f 996 ================== 997 */ 998 void CL_Disconnect_f( void ) { 999 SCR_StopCinematic(); 1000 Cvar_Set("ui_singlePlayerActive", "0"); 1001 if ( cls.state != CA_DISCONNECTED && cls.state != CA_CINEMATIC ) { 1002 Com_Error (ERR_DISCONNECT, "Disconnected from server"); 1003 } 1004 } 1005 1006 1007 /* 1008 ================ 1009 CL_Reconnect_f 1010 1011 ================ 1012 */ 1013 void CL_Reconnect_f( void ) { 1014 if ( !strlen( cls.servername ) || !strcmp( cls.servername, "localhost" ) ) { 1015 Com_Printf( "Can't reconnect to localhost.\n" ); 1016 return; 1017 } 1018 Cvar_Set("ui_singlePlayerActive", "0"); 1019 Cbuf_AddText( va("connect %s\n", cls.servername ) ); 1020 } 1021 1022 /* 1023 ================ 1024 CL_Connect_f 1025 1026 ================ 1027 */ 1028 void CL_Connect_f( void ) { 1029 char *server; 1030 1031 if ( Cmd_Argc() != 2 ) { 1032 Com_Printf( "usage: connect [server]\n"); 1033 return; 1034 } 1035 1036 Cvar_Set("ui_singlePlayerActive", "0"); 1037 1038 // fire a message off to the motd server 1039 CL_RequestMotd(); 1040 1041 // clear any previous "server full" type messages 1042 clc.serverMessage[0] = 0; 1043 1044 server = Cmd_Argv (1); 1045 1046 if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) { 1047 // if running a local server, kill it 1048 SV_Shutdown( "Server quit\n" ); 1049 } 1050 1051 // make sure a local server is killed 1052 Cvar_Set( "sv_killserver", "1" ); 1053 SV_Frame( 0 ); 1054 1055 CL_Disconnect( qtrue ); 1056 Con_Close(); 1057 1058 /* MrE: 2000-09-13: now called in CL_DownloadsComplete 1059 CL_FlushMemory( ); 1060 */ 1061 1062 Q_strncpyz( cls.servername, server, sizeof(cls.servername) ); 1063 1064 if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) { 1065 Com_Printf ("Bad server address\n"); 1066 cls.state = CA_DISCONNECTED; 1067 return; 1068 } 1069 if (clc.serverAddress.port == 0) { 1070 clc.serverAddress.port = BigShort( PORT_SERVER ); 1071 } 1072 Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", cls.servername, 1073 clc.serverAddress.ip[0], clc.serverAddress.ip[1], 1074 clc.serverAddress.ip[2], clc.serverAddress.ip[3], 1075 BigShort( clc.serverAddress.port ) ); 1076 1077 // if we aren't playing on a lan, we need to authenticate 1078 // with the cd key 1079 if ( NET_IsLocalAddress( clc.serverAddress ) ) { 1080 cls.state = CA_CHALLENGING; 1081 } else { 1082 cls.state = CA_CONNECTING; 1083 } 1084 1085 cls.keyCatchers = 0; 1086 clc.connectTime = -99999; // CL_CheckForResend() will fire immediately 1087 clc.connectPacketCount = 0; 1088 1089 // server connection string 1090 Cvar_Set( "cl_currentServerAddress", server ); 1091 } 1092 1093 1094 /* 1095 ===================== 1096 CL_Rcon_f 1097 1098 Send the rest of the command line over as 1099 an unconnected command. 1100 ===================== 1101 */ 1102 void CL_Rcon_f( void ) { 1103 char message[1024]; 1104 netadr_t to; 1105 1106 if ( !rcon_client_password->string ) { 1107 Com_Printf ("You must set 'rconpassword' before\n" 1108 "issuing an rcon command.\n"); 1109 return; 1110 } 1111 1112 message[0] = -1; 1113 message[1] = -1; 1114 message[2] = -1; 1115 message[3] = -1; 1116 message[4] = 0; 1117 1118 strcat (message, "rcon "); 1119 1120 strcat (message, rcon_client_password->string); 1121 strcat (message, " "); 1122 1123 // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543 1124 strcat (message, Cmd_Cmd()+5); 1125 1126 if ( cls.state >= CA_CONNECTED ) { 1127 to = clc.netchan.remoteAddress; 1128 } else { 1129 if (!strlen(rconAddress->string)) { 1130 Com_Printf ("You must either be connected,\n" 1131 "or set the 'rconAddress' cvar\n" 1132 "to issue rcon commands\n"); 1133 1134 return; 1135 } 1136 NET_StringToAdr (rconAddress->string, &to); 1137 if (to.port == 0) { 1138 to.port = BigShort (PORT_SERVER); 1139 } 1140 } 1141 1142 NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to); 1143 } 1144 1145 /* 1146 ================= 1147 CL_SendPureChecksums 1148 ================= 1149 */ 1150 void CL_SendPureChecksums( void ) { 1151 const char *pChecksums; 1152 char cMsg[MAX_INFO_VALUE]; 1153 int i; 1154 1155 // if we are pure we need to send back a command with our referenced pk3 checksums 1156 pChecksums = FS_ReferencedPakPureChecksums(); 1157 1158 // "cp" 1159 // "Yf" 1160 Com_sprintf(cMsg, sizeof(cMsg), "Yf "); 1161 Q_strcat(cMsg, sizeof(cMsg), va("%d ", cl.serverId) ); 1162 Q_strcat(cMsg, sizeof(cMsg), pChecksums); 1163 for (i = 0; i < 2; i++) { 1164 cMsg[i] += 10; 1165 } 1166 CL_AddReliableCommand( cMsg ); 1167 } 1168 1169 /* 1170 ================= 1171 CL_ResetPureClientAtServer 1172 ================= 1173 */ 1174 void CL_ResetPureClientAtServer( void ) { 1175 CL_AddReliableCommand( va("vdr") ); 1176 } 1177 1178 /* 1179 ================= 1180 CL_Vid_Restart_f 1181 1182 Restart the video subsystem 1183 1184 we also have to reload the UI and CGame because the renderer 1185 doesn't know what graphics to reload 1186 ================= 1187 */ 1188 void CL_Vid_Restart_f( void ) { 1189 1190 // don't let them loop during the restart 1191 S_StopAllSounds(); 1192 // shutdown the UI 1193 CL_ShutdownUI(); 1194 // shutdown the CGame 1195 CL_ShutdownCGame(); 1196 // shutdown the renderer and clear the renderer interface 1197 CL_ShutdownRef(); 1198 // client is no longer pure untill new checksums are sent 1199 CL_ResetPureClientAtServer(); 1200 // clear pak references 1201 FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF ); 1202 // reinitialize the filesystem if the game directory or checksum has changed 1203 FS_ConditionalRestart( clc.checksumFeed ); 1204 1205 cls.rendererStarted = qfalse; 1206 cls.uiStarted = qfalse; 1207 cls.cgameStarted = qfalse; 1208 cls.soundRegistered = qfalse; 1209 1210 // unpause so the cgame definately gets a snapshot and renders a frame 1211 Cvar_Set( "cl_paused", "0" ); 1212 1213 // if not running a server clear the whole hunk 1214 if ( !com_sv_running->integer ) { 1215 // clear the whole hunk 1216 Hunk_Clear(); 1217 } 1218 else { 1219 // clear all the client data on the hunk 1220 Hunk_ClearToMark(); 1221 } 1222 1223 // initialize the renderer interface 1224 CL_InitRef(); 1225 1226 // startup all the client stuff 1227 CL_StartHunkUsers(); 1228 1229 // start the cgame if connected 1230 if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) { 1231 cls.cgameStarted = qtrue; 1232 CL_InitCGame(); 1233 // send pure checksums 1234 CL_SendPureChecksums(); 1235 } 1236 } 1237 1238 /* 1239 ================= 1240 CL_Snd_Restart_f 1241 1242 Restart the sound subsystem 1243 The cgame and game must also be forced to restart because 1244 handles will be invalid 1245 ================= 1246 */ 1247 void CL_Snd_Restart_f( void ) { 1248 S_Shutdown(); 1249 S_Init(); 1250 1251 CL_Vid_Restart_f(); 1252 } 1253 1254 1255 /* 1256 ================== 1257 CL_PK3List_f 1258 ================== 1259 */ 1260 void CL_OpenedPK3List_f( void ) { 1261 Com_Printf("Opened PK3 Names: %s\n", FS_LoadedPakNames()); 1262 } 1263 1264 /* 1265 ================== 1266 CL_PureList_f 1267 ================== 1268 */ 1269 void CL_ReferencedPK3List_f( void ) { 1270 Com_Printf("Referenced PK3 Names: %s\n", FS_ReferencedPakNames()); 1271 } 1272 1273 /* 1274 ================== 1275 CL_Configstrings_f 1276 ================== 1277 */ 1278 void CL_Configstrings_f( void ) { 1279 int i; 1280 int ofs; 1281 1282 if ( cls.state != CA_ACTIVE ) { 1283 Com_Printf( "Not connected to a server.\n"); 1284 return; 1285 } 1286 1287 for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) { 1288 ofs = cl.gameState.stringOffsets[ i ]; 1289 if ( !ofs ) { 1290 continue; 1291 } 1292 Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs ); 1293 } 1294 } 1295 1296 /* 1297 ============== 1298 CL_Clientinfo_f 1299 ============== 1300 */ 1301 void CL_Clientinfo_f( void ) { 1302 Com_Printf( "--------- Client Information ---------\n" ); 1303 Com_Printf( "state: %i\n", cls.state ); 1304 Com_Printf( "Server: %s\n", cls.servername ); 1305 Com_Printf ("User info settings:\n"); 1306 Info_Print( Cvar_InfoString( CVAR_USERINFO ) ); 1307 Com_Printf( "--------------------------------------\n" ); 1308 } 1309 1310 1311 //==================================================================== 1312 1313 /* 1314 ================= 1315 CL_DownloadsComplete 1316 1317 Called when all downloading has been completed 1318 ================= 1319 */ 1320 void CL_DownloadsComplete( void ) { 1321 1322 // if we downloaded files we need to restart the file system 1323 if (clc.downloadRestart) { 1324 clc.downloadRestart = qfalse; 1325 1326 FS_Restart(clc.checksumFeed); // We possibly downloaded a pak, restart the file system to load it 1327 1328 // inform the server so we get new gamestate info 1329 CL_AddReliableCommand( "donedl" ); 1330 1331 // by sending the donedl command we request a new gamestate 1332 // so we don't want to load stuff yet 1333 return; 1334 } 1335 1336 // let the client game init and load data 1337 cls.state = CA_LOADING; 1338 1339 // Pump the loop, this may change gamestate! 1340 Com_EventLoop(); 1341 1342 // if the gamestate was changed by calling Com_EventLoop 1343 // then we loaded everything already and we don't want to do it again. 1344 if ( cls.state != CA_LOADING ) { 1345 return; 1346 } 1347 1348 // starting to load a map so we get out of full screen ui mode 1349 Cvar_Set("r_uiFullScreen", "0"); 1350 1351 // flush client memory and start loading stuff 1352 // this will also (re)load the UI 1353 // if this is a local client then only the client part of the hunk 1354 // will be cleared, note that this is done after the hunk mark has been set 1355 CL_FlushMemory(); 1356 1357 // initialize the CGame 1358 cls.cgameStarted = qtrue; 1359 CL_InitCGame(); 1360 1361 // set pure checksums 1362 CL_SendPureChecksums(); 1363 1364 CL_WritePacket(); 1365 CL_WritePacket(); 1366 CL_WritePacket(); 1367 } 1368 1369 /* 1370 ================= 1371 CL_BeginDownload 1372 1373 Requests a file to download from the server. Stores it in the current 1374 game directory. 1375 ================= 1376 */ 1377 void CL_BeginDownload( const char *localName, const char *remoteName ) { 1378 1379 Com_DPrintf("***** CL_BeginDownload *****\n" 1380 "Localname: %s\n" 1381 "Remotename: %s\n" 1382 "****************************\n", localName, remoteName); 1383 1384 Q_strncpyz ( clc.downloadName, localName, sizeof(clc.downloadName) ); 1385 Com_sprintf( clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName ); 1386 1387 // Set so UI gets access to it 1388 Cvar_Set( "cl_downloadName", remoteName ); 1389 Cvar_Set( "cl_downloadSize", "0" ); 1390 Cvar_Set( "cl_downloadCount", "0" ); 1391 Cvar_SetValue( "cl_downloadTime", cls.realtime ); 1392 1393 clc.downloadBlock = 0; // Starting new file 1394 clc.downloadCount = 0; 1395 1396 CL_AddReliableCommand( va("download %s", remoteName) ); 1397 } 1398 1399 /* 1400 ================= 1401 CL_NextDownload 1402 1403 A download completed or failed 1404 ================= 1405 */ 1406 void CL_NextDownload(void) { 1407 char *s; 1408 char *remoteName, *localName; 1409 1410 // We are looking to start a download here 1411 if (*clc.downloadList) { 1412 s = clc.downloadList; 1413 1414 // format is: 1415 // @remotename@localname@remotename@localname, etc. 1416 1417 if (*s == '@') 1418 s++; 1419 remoteName = s; 1420 1421 if ( (s = strchr(s, '@')) == NULL ) { 1422 CL_DownloadsComplete(); 1423 return; 1424 } 1425 1426 *s++ = 0; 1427 localName = s; 1428 if ( (s = strchr(s, '@')) != NULL ) 1429 *s++ = 0; 1430 else 1431 s = localName + strlen(localName); // point at the nul byte 1432 1433 CL_BeginDownload( localName, remoteName ); 1434 1435 clc.downloadRestart = qtrue; 1436 1437 // move over the rest 1438 memmove( clc.downloadList, s, strlen(s) + 1); 1439 1440 return; 1441 } 1442 1443 CL_DownloadsComplete(); 1444 } 1445 1446 /* 1447 ================= 1448 CL_InitDownloads 1449 1450 After receiving a valid game state, we valid the cgame and local zip files here 1451 and determine if we need to download them 1452 ================= 1453 */ 1454 void CL_InitDownloads(void) { 1455 char missingfiles[1024]; 1456 1457 if ( !cl_allowDownload->integer ) 1458 { 1459 // autodownload is disabled on the client 1460 // but it's possible that some referenced files on the server are missing 1461 if (FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) ) 1462 { 1463 // NOTE TTimo I would rather have that printed as a modal message box 1464 // but at this point while joining the game we don't know wether we will successfully join or not 1465 Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s" 1466 "You might not be able to join the game\n" 1467 "Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles ); 1468 } 1469 } 1470 else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ) , qtrue ) ) { 1471 1472 Com_Printf("Need paks: %s\n", clc.downloadList ); 1473 1474 if ( *clc.downloadList ) { 1475 // if autodownloading is not enabled on the server 1476 cls.state = CA_CONNECTED; 1477 CL_NextDownload(); 1478 return; 1479 } 1480 1481 } 1482 1483 CL_DownloadsComplete(); 1484 } 1485 1486 /* 1487 ================= 1488 CL_CheckForResend 1489 1490 Resend a connect message if the last one has timed out 1491 ================= 1492 */ 1493 void CL_CheckForResend( void ) { 1494 int port, i; 1495 char info[MAX_INFO_STRING]; 1496 char data[MAX_INFO_STRING]; 1497 1498 // don't send anything if playing back a demo 1499 if ( clc.demoplaying ) { 1500 return; 1501 } 1502 1503 // resend if we haven't gotten a reply yet 1504 if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) { 1505 return; 1506 } 1507 1508 if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) { 1509 return; 1510 } 1511 1512 clc.connectTime = cls.realtime; // for retransmit requests 1513 clc.connectPacketCount++; 1514 1515 1516 switch ( cls.state ) { 1517 case CA_CONNECTING: 1518 // requesting a challenge 1519 if ( !Sys_IsLANAddress( clc.serverAddress ) ) { 1520 CL_RequestAuthorization(); 1521 } 1522 NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "getchallenge"); 1523 break; 1524 1525 case CA_CHALLENGING: 1526 // sending back the challenge 1527 port = Cvar_VariableValue ("net_qport"); 1528 1529 Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) ); 1530 Info_SetValueForKey( info, "protocol", va("%i", PROTOCOL_VERSION ) ); 1531 Info_SetValueForKey( info, "qport", va("%i", port ) ); 1532 Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) ); 1533 1534 strcpy(data, "connect "); 1535 // TTimo adding " " around the userinfo string to avoid truncated userinfo on the server 1536 // (Com_TokenizeString tokenizes around spaces) 1537 data[8] = '"'; 1538 1539 for(i=0;i<strlen(info);i++) { 1540 data[9+i] = info[i]; // + (clc.challenge)&0x3; 1541 } 1542 data[9+i] = '"'; 1543 data[10+i] = 0; 1544 1545 // NOTE TTimo don't forget to set the right data length! 1546 NET_OutOfBandData( NS_CLIENT, clc.serverAddress, &data[0], i+10 ); 1547 // the most current userinfo has been sent, so watch for any 1548 // newer changes to userinfo variables 1549 cvar_modifiedFlags &= ~CVAR_USERINFO; 1550 break; 1551 1552 default: 1553 Com_Error( ERR_FATAL, "CL_CheckForResend: bad cls.state" ); 1554 } 1555 } 1556 1557 /* 1558 =================== 1559 CL_DisconnectPacket 1560 1561 Sometimes the server can drop the client and the netchan based 1562 disconnect can be lost. If the client continues to send packets 1563 to the server, the server will send out of band disconnect packets 1564 to the client so it doesn't have to wait for the full timeout period. 1565 =================== 1566 */ 1567 void CL_DisconnectPacket( netadr_t from ) { 1568 if ( cls.state < CA_AUTHORIZING ) { 1569 return; 1570 } 1571 1572 // if not from our server, ignore it 1573 if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) { 1574 return; 1575 } 1576 1577 // if we have received packets within three seconds, ignore it 1578 // (it might be a malicious spoof) 1579 if ( cls.realtime - clc.lastPacketTime < 3000 ) { 1580 return; 1581 } 1582 1583 // drop the connection 1584 Com_Printf( "Server disconnected for unknown reason\n" ); 1585 Cvar_Set("com_errorMessage", "Server disconnected for unknown reason\n" ); 1586 CL_Disconnect( qtrue ); 1587 } 1588 1589 1590 /* 1591 =================== 1592 CL_MotdPacket 1593 1594 =================== 1595 */ 1596 void CL_MotdPacket( netadr_t from ) { 1597 char *challenge; 1598 char *info; 1599 1600 // if not from our server, ignore it 1601 if ( !NET_CompareAdr( from, cls.updateServer ) ) { 1602 return; 1603 } 1604 1605 info = Cmd_Argv(1); 1606 1607 // check challenge 1608 challenge = Info_ValueForKey( info, "challenge" ); 1609 if ( strcmp( challenge, cls.updateChallenge ) ) { 1610 return; 1611 } 1612 1613 challenge = Info_ValueForKey( info, "motd" ); 1614 1615 Q_strncpyz( cls.updateInfoString, info, sizeof( cls.updateInfoString ) ); 1616 Cvar_Set( "cl_motdString", challenge ); 1617 } 1618 1619 /* 1620 =================== 1621 CL_InitServerInfo 1622 =================== 1623 */ 1624 void CL_InitServerInfo( serverInfo_t *server, serverAddress_t *address ) { 1625 server->adr.type = NA_IP; 1626 server->adr.ip[0] = address->ip[0]; 1627 server->adr.ip[1] = address->ip[1]; 1628 server->adr.ip[2] = address->ip[2]; 1629 server->adr.ip[3] = address->ip[3]; 1630 server->adr.port = address->port; 1631 server->clients = 0; 1632 server->hostName[0] = '\0'; 1633 server->mapName[0] = '\0'; 1634 server->maxClients = 0; 1635 server->maxPing = 0; 1636 server->minPing = 0; 1637 server->ping = -1; 1638 server->game[0] = '\0'; 1639 server->gameType = 0; 1640 server->netType = 0; 1641 } 1642 1643 #define MAX_SERVERSPERPACKET 256 1644 1645 /* 1646 =================== 1647 CL_ServersResponsePacket 1648 =================== 1649 */ 1650 void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) { 1651 int i, count, max, total; 1652 serverAddress_t addresses[MAX_SERVERSPERPACKET]; 1653 int numservers; 1654 byte* buffptr; 1655 byte* buffend; 1656 1657 Com_Printf("CL_ServersResponsePacket\n"); 1658 1659 if (cls.numglobalservers == -1) { 1660 // state to detect lack of servers or lack of response 1661 cls.numglobalservers = 0; 1662 cls.numGlobalServerAddresses = 0; 1663 } 1664 1665 if (cls.nummplayerservers == -1) { 1666 cls.nummplayerservers = 0; 1667 } 1668 1669 // parse through server response string 1670 numservers = 0; 1671 buffptr = msg->data; 1672 buffend = buffptr + msg->cursize; 1673 while (buffptr+1 < buffend) { 1674 // advance to initial token 1675 do { 1676 if (*buffptr++ == '\\') 1677 break; 1678 } 1679 while (buffptr < buffend); 1680 1681 if ( buffptr >= buffend - 6 ) { 1682 break; 1683 } 1684 1685 // parse out ip 1686 addresses[numservers].ip[0] = *buffptr++; 1687 addresses[numservers].ip[1] = *buffptr++; 1688 addresses[numservers].ip[2] = *buffptr++; 1689 addresses[numservers].ip[3] = *buffptr++; 1690 1691 // parse out port 1692 addresses[numservers].port = (*buffptr++)<<8; 1693 addresses[numservers].port += *buffptr++; 1694 addresses[numservers].port = BigShort( addresses[numservers].port ); 1695 1696 // syntax check 1697 if (*buffptr != '\\') { 1698 break; 1699 } 1700 1701 Com_DPrintf( "server: %d ip: %d.%d.%d.%d:%d\n",numservers, 1702 addresses[numservers].ip[0], 1703 addresses[numservers].ip[1], 1704 addresses[numservers].ip[2], 1705 addresses[numservers].ip[3], 1706 addresses[numservers].port ); 1707 1708 numservers++; 1709 if (numservers >= MAX_SERVERSPERPACKET) { 1710 break; 1711 } 1712 1713 // parse out EOT 1714 if (buffptr[1] == 'E' && buffptr[2] == 'O' && buffptr[3] == 'T') { 1715 break; 1716 } 1717 } 1718 1719 if (cls.masterNum == 0) { 1720 count = cls.numglobalservers; 1721 max = MAX_GLOBAL_SERVERS; 1722 } else { 1723 count = cls.nummplayerservers; 1724 max = MAX_OTHER_SERVERS; 1725 } 1726 1727 for (i = 0; i < numservers && count < max; i++) { 1728 // build net address 1729 serverInfo_t *server = (cls.masterNum == 0) ? &cls.globalServers[count] : &cls.mplayerServers[count]; 1730 1731 CL_InitServerInfo( server, &addresses[i] ); 1732 // advance to next slot 1733 count++; 1734 } 1735 1736 // if getting the global list 1737 if (cls.masterNum == 0) { 1738 if ( cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS ) { 1739 // if we couldn't store the servers in the main list anymore 1740 for (; i < numservers && count >= max; i++) { 1741 serverAddress_t *addr; 1742 // just store the addresses in an additional list 1743 addr = &cls.globalServerAddresses[cls.numGlobalServerAddresses++]; 1744 addr->ip[0] = addresses[i].ip[0]; 1745 addr->ip[1] = addresses[i].ip[1]; 1746 addr->ip[2] = addresses[i].ip[2]; 1747 addr->ip[3] = addresses[i].ip[3]; 1748 addr->port = addresses[i].port; 1749 } 1750 } 1751 } 1752 1753 if (cls.masterNum == 0) { 1754 cls.numglobalservers = count; 1755 total = count + cls.numGlobalServerAddresses; 1756 } else { 1757 cls.nummplayerservers = count; 1758 total = count; 1759 } 1760 1761 Com_Printf("%d servers parsed (total %d)\n", numservers, total); 1762 } 1763 1764 /* 1765 ================= 1766 CL_ConnectionlessPacket 1767 1768 Responses to broadcasts, etc 1769 ================= 1770 */ 1771 void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) { 1772 char *s; 1773 char *c; 1774 1775 MSG_BeginReadingOOB( msg ); 1776 MSG_ReadLong( msg ); // skip the -1 1777 1778 s = MSG_ReadStringLine( msg ); 1779 1780 Cmd_TokenizeString( s ); 1781 1782 c = Cmd_Argv(0); 1783 1784 Com_DPrintf ("CL packet %s: %s\n", NET_AdrToString(from), c); 1785 1786 // challenge from the server we are connecting to 1787 if ( !Q_stricmp(c, "challengeResponse") ) { 1788 if ( cls.state != CA_CONNECTING ) { 1789 Com_Printf( "Unwanted challenge response received. Ignored.\n" ); 1790 } else { 1791 // start sending challenge repsonse instead of challenge request packets 1792 clc.challenge = atoi(Cmd_Argv(1)); 1793 cls.state = CA_CHALLENGING; 1794 clc.connectPacketCount = 0; 1795 clc.connectTime = -99999; 1796 1797 // take this address as the new server address. This allows 1798 // a server proxy to hand off connections to multiple servers 1799 clc.serverAddress = from; 1800 Com_DPrintf ("challengeResponse: %d\n", clc.challenge); 1801 } 1802 return; 1803 } 1804 1805 // server connection 1806 if ( !Q_stricmp(c, "connectResponse") ) { 1807 if ( cls.state >= CA_CONNECTED ) { 1808 Com_Printf ("Dup connect received. Ignored.\n"); 1809 return; 1810 } 1811 if ( cls.state != CA_CHALLENGING ) { 1812 Com_Printf ("connectResponse packet while not connecting. Ignored.\n"); 1813 return; 1814 } 1815 if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) { 1816 Com_Printf( "connectResponse from a different address. Ignored.\n" ); 1817 Com_Printf( "%s should have been %s\n", NET_AdrToString( from ), 1818 NET_AdrToString( clc.serverAddress ) ); 1819 return; 1820 } 1821 Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) ); 1822 cls.state = CA_CONNECTED; 1823 clc.lastPacketSentTime = -9999; // send first packet immediately 1824 return; 1825 } 1826 1827 // server responding to an info broadcast 1828 if ( !Q_stricmp(c, "infoResponse") ) { 1829 CL_ServerInfoPacket( from, msg ); 1830 return; 1831 } 1832 1833 // server responding to a get playerlist 1834 if ( !Q_stricmp(c, "statusResponse") ) { 1835 CL_ServerStatusResponse( from, msg ); 1836 return; 1837 } 1838 1839 // a disconnect message from the server, which will happen if the server 1840 // dropped the connection but it is still getting packets from us 1841 if (!Q_stricmp(c, "disconnect")) { 1842 CL_DisconnectPacket( from ); 1843 return; 1844 } 1845 1846 // echo request from server 1847 if ( !Q_stricmp(c, "echo") ) { 1848 NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) ); 1849 return; 1850 } 1851 1852 // cd check 1853 if ( !Q_stricmp(c, "keyAuthorize") ) { 1854 // we don't use these now, so dump them on the floor 1855 return; 1856 } 1857 1858 // global MOTD from id 1859 if ( !Q_stricmp(c, "motd") ) { 1860 CL_MotdPacket( from ); 1861 return; 1862 } 1863 1864 // echo request from server 1865 if ( !Q_stricmp(c, "print") ) { 1866 s = MSG_ReadString( msg ); 1867 Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) ); 1868 Com_Printf( "%s", s ); 1869 return; 1870 } 1871 1872 // echo request from server 1873 if ( !Q_strncmp(c, "getserversResponse", 18) ) { 1874 CL_ServersResponsePacket( from, msg ); 1875 return; 1876 } 1877 1878 Com_DPrintf ("Unknown connectionless packet command.\n"); 1879 } 1880 1881 1882 /* 1883 ================= 1884 CL_PacketEvent 1885 1886 A packet has arrived from the main event loop 1887 ================= 1888 */ 1889 void CL_PacketEvent( netadr_t from, msg_t *msg ) { 1890 int headerBytes; 1891 1892 clc.lastPacketTime = cls.realtime; 1893 1894 if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) { 1895 CL_ConnectionlessPacket( from, msg ); 1896 return; 1897 } 1898 1899 if ( cls.state < CA_CONNECTED ) { 1900 return; // can't be a valid sequenced packet 1901 } 1902 1903 if ( msg->cursize < 4 ) { 1904 Com_Printf ("%s: Runt packet\n",NET_AdrToString( from )); 1905 return; 1906 } 1907 1908 // 1909 // packet from server 1910 // 1911 if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) { 1912 Com_DPrintf ("%s:sequenced packet without connection\n" 1913 ,NET_AdrToString( from ) ); 1914 // FIXME: send a client disconnect? 1915 return; 1916 } 1917 1918 if (!CL_Netchan_Process( &clc.netchan, msg) ) { 1919 return; // out of order, duplicated, etc 1920 } 1921 1922 // the header is different lengths for reliable and unreliable messages 1923 headerBytes = msg->readcount; 1924 1925 // track the last message received so it can be returned in 1926 // client messages, allowing the server to detect a dropped 1927 // gamestate 1928 clc.serverMessageSequence = LittleLong( *(int *)msg->data ); 1929 1930 clc.lastPacketTime = cls.realtime; 1931 CL_ParseServerMessage( msg ); 1932 1933 // 1934 // we don't know if it is ok to save a demo message until 1935 // after we have parsed the frame 1936 // 1937 if ( clc.demorecording && !clc.demowaiting ) { 1938 CL_WriteDemoMessage( msg, headerBytes ); 1939 } 1940 } 1941 1942 /* 1943 ================== 1944 CL_CheckTimeout 1945 1946 ================== 1947 */ 1948 void CL_CheckTimeout( void ) { 1949 // 1950 // check timeout 1951 // 1952 if ( ( !cl_paused->integer || !sv_paused->integer ) 1953 && cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC 1954 && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) { 1955 if (++cl.timeoutcount > 5) { // timeoutcount saves debugger 1956 Com_Printf ("\nServer connection timed out.\n"); 1957 CL_Disconnect( qtrue ); 1958 return; 1959 } 1960 } else { 1961 cl.timeoutcount = 0; 1962 } 1963 } 1964 1965 1966 //============================================================================ 1967 1968 /* 1969 ================== 1970 CL_CheckUserinfo 1971 1972 ================== 1973 */ 1974 void CL_CheckUserinfo( void ) { 1975 // don't add reliable commands when not yet connected 1976 if ( cls.state < CA_CHALLENGING ) { 1977 return; 1978 } 1979 // don't overflow the reliable command buffer when paused 1980 if ( cl_paused->integer ) { 1981 return; 1982 } 1983 // send a reliable userinfo update if needed 1984 if ( cvar_modifiedFlags & CVAR_USERINFO ) { 1985 cvar_modifiedFlags &= ~CVAR_USERINFO; 1986 CL_AddReliableCommand( va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ) ); 1987 } 1988 1989 } 1990 1991 /* 1992 ================== 1993 CL_Frame 1994 1995 ================== 1996 */ 1997 void CL_Frame ( int msec ) { 1998 1999 if ( !com_cl_running->integer ) { 2000 return; 2001 } 2002 2003 if ( cls.cddialog ) { 2004 // bring up the cd error dialog if needed 2005 cls.cddialog = qfalse; 2006 VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD ); 2007 } else if ( cls.state == CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_UI ) 2008 && !com_sv_running->integer ) { 2009 // if disconnected, bring up the menu 2010 S_StopAllSounds(); 2011 VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN ); 2012 } 2013 2014 // if recording an avi, lock to a fixed fps 2015 if ( cl_avidemo->integer && msec) { 2016 // save the current screen 2017 if ( cls.state == CA_ACTIVE || cl_forceavidemo->integer) { 2018 Cbuf_ExecuteText( EXEC_NOW, "screenshot silent\n" ); 2019 } 2020 // fixed time for next frame' 2021 msec = (1000 / cl_avidemo->integer) * com_timescale->value; 2022 if (msec == 0) { 2023 msec = 1; 2024 } 2025 } 2026 2027 // save the msec before checking pause 2028 cls.realFrametime = msec; 2029 2030 // decide the simulation time 2031 cls.frametime = msec; 2032 2033 cls.realtime += cls.frametime; 2034 2035 if ( cl_timegraph->integer ) { 2036 SCR_DebugGraph ( cls.realFrametime * 0.25, 0 ); 2037 } 2038 2039 // see if we need to update any userinfo 2040 CL_CheckUserinfo(); 2041 2042 // if we haven't gotten a packet in a long time, 2043 // drop the connection 2044 CL_CheckTimeout(); 2045 2046 // send intentions now 2047 CL_SendCmd(); 2048 2049 // resend a connection request if necessary 2050 CL_CheckForResend(); 2051 2052 // decide on the serverTime to render 2053 CL_SetCGameTime(); 2054 2055 // update the screen 2056 SCR_UpdateScreen(); 2057 2058 // update audio 2059 S_Update(); 2060 2061 // advance local effects for next frame 2062 SCR_RunCinematic(); 2063 2064 Con_RunConsole(); 2065 2066 cls.framecount++; 2067 } 2068 2069 2070 //============================================================================ 2071 2072 /* 2073 ================ 2074 CL_RefPrintf 2075 2076 DLL glue 2077 ================ 2078 */ 2079 void QDECL CL_RefPrintf( int print_level, const char *fmt, ...) { 2080 va_list argptr; 2081 char msg[MAXPRINTMSG]; 2082 2083 va_start (argptr,fmt); 2084 Q_vsnprintf (msg, sizeof(msg), fmt, argptr); 2085 va_end (argptr); 2086 2087 if ( print_level == PRINT_ALL ) { 2088 Com_Printf ("%s", msg); 2089 } else if ( print_level == PRINT_WARNING ) { 2090 Com_Printf (S_COLOR_YELLOW "%s", msg); // yellow 2091 } else if ( print_level == PRINT_DEVELOPER ) { 2092 Com_DPrintf (S_COLOR_RED "%s", msg); // red 2093 } 2094 } 2095 2096 2097 2098 /* 2099 ============ 2100 CL_ShutdownRef 2101 ============ 2102 */ 2103 void CL_ShutdownRef( void ) { 2104 if ( !re.Shutdown ) { 2105 return; 2106 } 2107 re.Shutdown( qtrue ); 2108 Com_Memset( &re, 0, sizeof( re ) ); 2109 } 2110 2111 /* 2112 ============ 2113 CL_InitRenderer 2114 ============ 2115 */ 2116 void CL_InitRenderer( void ) { 2117 // this sets up the renderer and calls R_Init 2118 re.BeginRegistration( &cls.glconfig ); 2119 2120 // load character sets 2121 cls.charSetShader = re.RegisterShader( "gfx/2d/bigchars" ); 2122 cls.whiteShader = re.RegisterShader( "white" ); 2123 cls.consoleShader = re.RegisterShader( "console" ); 2124 g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2; 2125 g_consoleField.widthInChars = g_console_field_width; 2126 } 2127 2128 /* 2129 ============================ 2130 CL_StartHunkUsers 2131 2132 After the server has cleared the hunk, these will need to be restarted 2133 This is the only place that any of these functions are called from 2134 ============================ 2135 */ 2136 void CL_StartHunkUsers( void ) { 2137 if (!com_cl_running) { 2138 return; 2139 } 2140 2141 if ( !com_cl_running->integer ) { 2142 return; 2143 } 2144 2145 if ( !cls.rendererStarted ) { 2146 cls.rendererStarted = qtrue; 2147 CL_InitRenderer(); 2148 } 2149 2150 if ( !cls.soundStarted ) { 2151 cls.soundStarted = qtrue; 2152 S_Init(); 2153 } 2154 2155 if ( !cls.soundRegistered ) { 2156 cls.soundRegistered = qtrue; 2157 S_BeginRegistration(); 2158 } 2159 2160 if ( !cls.uiStarted ) { 2161 cls.uiStarted = qtrue; 2162 CL_InitUI(); 2163 } 2164 } 2165 2166 /* 2167 ============ 2168 CL_RefMalloc 2169 ============ 2170 */ 2171 void *CL_RefMalloc( int size ) { 2172 return Z_TagMalloc( size, TAG_RENDERER ); 2173 } 2174 2175 int CL_ScaledMilliseconds(void) { 2176 return Sys_Milliseconds()*com_timescale->value; 2177 } 2178 2179 /* 2180 ============ 2181 CL_InitRef 2182 ============ 2183 */ 2184 void CL_InitRef( void ) { 2185 refimport_t ri; 2186 refexport_t *ret; 2187 2188 Com_Printf( "----- Initializing Renderer ----\n" ); 2189 2190 ri.Cmd_AddCommand = Cmd_AddCommand; 2191 ri.Cmd_RemoveCommand = Cmd_RemoveCommand; 2192 ri.Cmd_Argc = Cmd_Argc; 2193 ri.Cmd_Argv = Cmd_Argv; 2194 ri.Cmd_ExecuteText = Cbuf_ExecuteText; 2195 ri.Printf = CL_RefPrintf; 2196 ri.Error = Com_Error; 2197 ri.Milliseconds = CL_ScaledMilliseconds; 2198 ri.Malloc = CL_RefMalloc; 2199 ri.Free = Z_Free; 2200 #ifdef HUNK_DEBUG 2201 ri.Hunk_AllocDebug = Hunk_AllocDebug; 2202 #else 2203 ri.Hunk_Alloc = Hunk_Alloc; 2204 #endif 2205 ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory; 2206 ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory; 2207 ri.CM_DrawDebugSurface = CM_DrawDebugSurface; 2208 ri.FS_ReadFile = FS_ReadFile; 2209 ri.FS_FreeFile = FS_FreeFile; 2210 ri.FS_WriteFile = FS_WriteFile; 2211 ri.FS_FreeFileList = FS_FreeFileList; 2212 ri.FS_ListFiles = FS_ListFiles; 2213 ri.FS_FileIsInPAK = FS_FileIsInPAK; 2214 ri.FS_FileExists = FS_FileExists; 2215 ri.Cvar_Get = Cvar_Get; 2216 ri.Cvar_Set = Cvar_Set; 2217 2218 // cinematic stuff 2219 2220 ri.CIN_UploadCinematic = CIN_UploadCinematic; 2221 ri.CIN_PlayCinematic = CIN_PlayCinematic; 2222 ri.CIN_RunCinematic = CIN_RunCinematic; 2223 2224 ret = GetRefAPI( REF_API_VERSION, &ri ); 2225 2226 #if defined __USEA3D && defined __A3D_GEOM 2227 hA3Dg_ExportRenderGeom (ret); 2228 #endif 2229 2230 Com_Printf( "-------------------------------\n"); 2231 2232 if ( !ret ) { 2233 Com_Error (ERR_FATAL, "Couldn't initialize refresh" ); 2234 } 2235 2236 re = *ret; 2237 2238 // unpause so the cgame definately gets a snapshot and renders a frame 2239 Cvar_Set( "cl_paused", "0" ); 2240 } 2241 2242 2243 //=========================================================================================== 2244 2245 2246 void CL_SetModel_f( void ) { 2247 char *arg; 2248 char name[256]; 2249 2250 arg = Cmd_Argv( 1 ); 2251 if (arg[0]) { 2252 Cvar_Set( "model", arg ); 2253 Cvar_Set( "headmodel", arg ); 2254 } else { 2255 Cvar_VariableStringBuffer( "model", name, sizeof(name) ); 2256 Com_Printf("model is set to %s\n", name); 2257 } 2258 } 2259 2260 /* 2261 ==================== 2262 CL_Init 2263 ==================== 2264 */ 2265 void CL_Init( void ) { 2266 Com_Printf( "----- Client Initialization -----\n" ); 2267 2268 Con_Init (); 2269 2270 CL_ClearState (); 2271 2272 cls.state = CA_DISCONNECTED; // no longer CA_UNINITIALIZED 2273 2274 cls.realtime = 0; 2275 2276 CL_InitInput (); 2277 2278 // 2279 // register our variables 2280 // 2281 cl_noprint = Cvar_Get( "cl_noprint", "0", 0 ); 2282 cl_motd = Cvar_Get ("cl_motd", "1", 0); 2283 2284 cl_timeout = Cvar_Get ("cl_timeout", "200", 0); 2285 2286 cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP ); 2287 cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP ); 2288 cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP ); 2289 cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP ); 2290 cl_freezeDemo = Cvar_Get ("cl_freezeDemo", "0", CVAR_TEMP ); 2291 rcon_client_password = Cvar_Get ("rconPassword", "", CVAR_TEMP ); 2292 cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP ); 2293 2294 cl_timedemo = Cvar_Get ("timedemo", "0", 0); 2295 cl_avidemo = Cvar_Get ("cl_avidemo", "0", 0); 2296 cl_forceavidemo = Cvar_Get ("cl_forceavidemo", "0", 0); 2297 2298 rconAddress = Cvar_Get ("rconAddress", "", 0); 2299 2300 cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE); 2301 cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE); 2302 cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0); 2303 2304 cl_maxpackets = Cvar_Get ("cl_maxpackets", "30", CVAR_ARCHIVE ); 2305 cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE ); 2306 2307 cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE); 2308 cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE); 2309 cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE); 2310 cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE ); 2311 2312 cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0); 2313 2314 cl_allowDownload = Cvar_Get ("cl_allowDownload", "0", CVAR_ARCHIVE); 2315 2316 cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0); 2317 #ifdef MACOS_X 2318 // In game video is REALLY slow in Mac OS X right now due to driver slowness 2319 cl_inGameVideo = Cvar_Get ("r_inGameVideo", "0", CVAR_ARCHIVE); 2320 #else 2321 cl_inGameVideo = Cvar_Get ("r_inGameVideo", "1", CVAR_ARCHIVE); 2322 #endif 2323 2324 cl_serverStatusResendTime = Cvar_Get ("cl_serverStatusResendTime", "750", 0); 2325 2326 // init autoswitch so the ui will have it correctly even 2327 // if the cgame hasn't been started 2328 Cvar_Get ("cg_autoswitch", "1", CVAR_ARCHIVE); 2329 2330 m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE); 2331 m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE); 2332 m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE); 2333 m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE); 2334 #ifdef MACOS_X 2335 // Input is jittery on OS X w/o this 2336 m_filter = Cvar_Get ("m_filter", "1", CVAR_ARCHIVE); 2337 #else 2338 m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE); 2339 #endif 2340 2341 cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM ); 2342 2343 Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE ); 2344 2345 2346 // userinfo 2347 Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE ); 2348 Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE ); 2349 Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE ); 2350 Cvar_Get ("model", "sarge", CVAR_USERINFO | CVAR_ARCHIVE ); 2351 Cvar_Get ("headmodel", "sarge", CVAR_USERINFO | CVAR_ARCHIVE ); 2352 Cvar_Get ("team_model", "james", CVAR_USERINFO | CVAR_ARCHIVE ); 2353 Cvar_Get ("team_headmodel", "*james", CVAR_USERINFO | CVAR_ARCHIVE ); 2354 Cvar_Get ("g_redTeam", "Stroggs", CVAR_SERVERINFO | CVAR_ARCHIVE); 2355 Cvar_Get ("g_blueTeam", "Pagans", CVAR_SERVERINFO | CVAR_ARCHIVE); 2356 Cvar_Get ("color1", "4", CVAR_USERINFO | CVAR_ARCHIVE ); 2357 Cvar_Get ("color2", "5", CVAR_USERINFO | CVAR_ARCHIVE ); 2358 Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE ); 2359 Cvar_Get ("teamtask", "0", CVAR_USERINFO ); 2360 Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE ); 2361 Cvar_Get ("cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE ); 2362 2363 Cvar_Get ("password", "", CVAR_USERINFO); 2364 Cvar_Get ("cg_predictItems", "1", CVAR_USERINFO | CVAR_ARCHIVE ); 2365 2366 2367 // cgame might not be initialized before menu is used 2368 Cvar_Get ("cg_viewsize", "100", CVAR_ARCHIVE ); 2369 2370 // 2371 // register our commands 2372 // 2373 Cmd_AddCommand ("cmd", CL_ForwardToServer_f); 2374 Cmd_AddCommand ("configstrings", CL_Configstrings_f); 2375 Cmd_AddCommand ("clientinfo", CL_Clientinfo_f); 2376 Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f); 2377 Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f); 2378 Cmd_AddCommand ("disconnect", CL_Disconnect_f); 2379 Cmd_AddCommand ("record", CL_Record_f); 2380 Cmd_AddCommand ("demo", CL_PlayDemo_f); 2381 Cmd_AddCommand ("cinematic", CL_PlayCinematic_f); 2382 Cmd_AddCommand ("stoprecord", CL_StopRecord_f); 2383 Cmd_AddCommand ("connect", CL_Connect_f); 2384 Cmd_AddCommand ("reconnect", CL_Reconnect_f); 2385 Cmd_AddCommand ("localservers", CL_LocalServers_f); 2386 Cmd_AddCommand ("globalservers", CL_GlobalServers_f); 2387 Cmd_AddCommand ("rcon", CL_Rcon_f); 2388 Cmd_AddCommand ("setenv", CL_Setenv_f ); 2389 Cmd_AddCommand ("ping", CL_Ping_f ); 2390 Cmd_AddCommand ("serverstatus", CL_ServerStatus_f ); 2391 Cmd_AddCommand ("showip", CL_ShowIP_f ); 2392 Cmd_AddCommand ("fs_openedList", CL_OpenedPK3List_f ); 2393 Cmd_AddCommand ("fs_referencedList", CL_ReferencedPK3List_f ); 2394 Cmd_AddCommand ("model", CL_SetModel_f ); 2395 CL_InitRef(); 2396 2397 SCR_Init (); 2398 2399 Cbuf_Execute (); 2400 2401 Cvar_Set( "cl_running", "1" ); 2402 2403 Com_Printf( "----- Client Initialization Complete -----\n" ); 2404 } 2405 2406 2407 /* 2408 =============== 2409 CL_Shutdown 2410 2411 =============== 2412 */ 2413 void CL_Shutdown( void ) { 2414 static qboolean recursive = qfalse; 2415 2416 Com_Printf( "----- CL_Shutdown -----\n" ); 2417 2418 if ( recursive ) { 2419 printf ("recursive shutdown\n"); 2420 return; 2421 } 2422 recursive = qtrue; 2423 2424 CL_Disconnect( qtrue ); 2425 2426 S_Shutdown(); 2427 CL_ShutdownRef(); 2428 2429 CL_ShutdownUI(); 2430 2431 Cmd_RemoveCommand ("cmd"); 2432 Cmd_RemoveCommand ("configstrings"); 2433 Cmd_RemoveCommand ("userinfo"); 2434 Cmd_RemoveCommand ("snd_restart"); 2435 Cmd_RemoveCommand ("vid_restart"); 2436 Cmd_RemoveCommand ("disconnect"); 2437 Cmd_RemoveCommand ("record"); 2438 Cmd_RemoveCommand ("demo"); 2439 Cmd_RemoveCommand ("cinematic"); 2440 Cmd_RemoveCommand ("stoprecord"); 2441 Cmd_RemoveCommand ("connect"); 2442 Cmd_RemoveCommand ("localservers"); 2443 Cmd_RemoveCommand ("globalservers"); 2444 Cmd_RemoveCommand ("rcon"); 2445 Cmd_RemoveCommand ("setenv"); 2446 Cmd_RemoveCommand ("ping"); 2447 Cmd_RemoveCommand ("serverstatus"); 2448 Cmd_RemoveCommand ("showip"); 2449 Cmd_RemoveCommand ("model"); 2450 2451 Cvar_Set( "cl_running", "0" ); 2452 2453 recursive = qfalse; 2454 2455 Com_Memset( &cls, 0, sizeof( cls ) ); 2456 2457 Com_Printf( "-----------------------\n" ); 2458 2459 } 2460 2461 static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) { 2462 if (server) { 2463 if (info) { 2464 server->clients = atoi(Info_ValueForKey(info, "clients")); 2465 Q_strncpyz(server->hostName,Info_ValueForKey(info, "hostname"), MAX_NAME_LENGTH); 2466 Q_strncpyz(server->mapName, Info_ValueForKey(info, "mapname"), MAX_NAME_LENGTH); 2467 server->maxClients = atoi(Info_ValueForKey(info, "sv_maxclients")); 2468 Q_strncpyz(server->game,Info_ValueForKey(info, "game"), MAX_NAME_LENGTH); 2469 server->gameType = atoi(Info_ValueForKey(info, "gametype")); 2470 server->netType = atoi(Info_ValueForKey(info, "nettype")); 2471 server->minPing = atoi(Info_ValueForKey(info, "minping")); 2472 server->maxPing = atoi(Info_ValueForKey(info, "maxping")); 2473 server->punkbuster = atoi(Info_ValueForKey(info, "punkbuster")); 2474 } 2475 server->ping = ping; 2476 } 2477 } 2478 2479 static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping) { 2480 int i; 2481 2482 for (i = 0; i < MAX_OTHER_SERVERS; i++) { 2483 if (NET_CompareAdr(from, cls.localServers[i].adr)) { 2484 CL_SetServerInfo(&cls.localServers[i], info, ping); 2485 } 2486 } 2487 2488 for (i = 0; i < MAX_OTHER_SERVERS; i++) { 2489 if (NET_CompareAdr(from, cls.mplayerServers[i].adr)) { 2490 CL_SetServerInfo(&cls.mplayerServers[i], info, ping); 2491 } 2492 } 2493 2494 for (i = 0; i < MAX_GLOBAL_SERVERS; i++) { 2495 if (NET_CompareAdr(from, cls.globalServers[i].adr)) { 2496 CL_SetServerInfo(&cls.globalServers[i], info, ping); 2497 } 2498 } 2499 2500 for (i = 0; i < MAX_OTHER_SERVERS; i++) { 2501 if (NET_CompareAdr(from, cls.favoriteServers[i].adr)) { 2502 CL_SetServerInfo(&cls.favoriteServers[i], info, ping); 2503 } 2504 } 2505 2506 } 2507 2508 /* 2509 =================== 2510 CL_ServerInfoPacket 2511 =================== 2512 */ 2513 void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) { 2514 int i, type; 2515 char info[MAX_INFO_STRING]; 2516 char* str; 2517 char *infoString; 2518 int prot; 2519 2520 infoString = MSG_ReadString( msg ); 2521 2522 // if this isn't the correct protocol version, ignore it 2523 prot = atoi( Info_ValueForKey( infoString, "protocol" ) ); 2524 if ( prot != PROTOCOL_VERSION ) { 2525 Com_DPrintf( "Different protocol info packet: %s\n", infoString ); 2526 return; 2527 } 2528 2529 // iterate servers waiting for ping response 2530 for (i=0; i<MAX_PINGREQUESTS; i++) 2531 { 2532 if ( cl_pinglist[i].adr.port && !cl_pinglist[i].time && NET_CompareAdr( from, cl_pinglist[i].adr ) ) 2533 { 2534 // calc ping time 2535 cl_pinglist[i].time = cls.realtime - cl_pinglist[i].start + 1; 2536 Com_DPrintf( "ping time %dms from %s\n", cl_pinglist[i].time, NET_AdrToString( from ) ); 2537 2538 // save of info 2539 Q_strncpyz( cl_pinglist[i].info, infoString, sizeof( cl_pinglist[i].info ) ); 2540 2541 // tack on the net type 2542 // NOTE: make sure these types are in sync with the netnames strings in the UI 2543 switch (from.type) 2544 { 2545 case NA_BROADCAST: 2546 case NA_IP: 2547 str = "udp"; 2548 type = 1; 2549 break; 2550 2551 case NA_IPX: 2552 case NA_BROADCAST_IPX: 2553 str = "ipx"; 2554 type = 2; 2555 break; 2556 2557 default: 2558 str = "???"; 2559 type = 0; 2560 break; 2561 } 2562 Info_SetValueForKey( cl_pinglist[i].info, "nettype", va("%d", type) ); 2563 CL_SetServerInfoByAddress(from, infoString, cl_pinglist[i].time); 2564 2565 return; 2566 } 2567 } 2568 2569 // if not just sent a local broadcast or pinging local servers 2570 if (cls.pingUpdateSource != AS_LOCAL) { 2571 return; 2572 } 2573 2574 for ( i = 0 ; i < MAX_OTHER_SERVERS ; i++ ) { 2575 // empty slot 2576 if ( cls.localServers[i].adr.port == 0 ) { 2577 break; 2578 } 2579 2580 // avoid duplicate 2581 if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) { 2582 return; 2583 } 2584 } 2585 2586 if ( i == MAX_OTHER_SERVERS ) { 2587 Com_DPrintf( "MAX_OTHER_SERVERS hit, dropping infoResponse\n" ); 2588 return; 2589 } 2590 2591 // add this to the list 2592 cls.numlocalservers = i+1; 2593 cls.localServers[i].adr = from; 2594 cls.localServers[i].clients = 0; 2595 cls.localServers[i].hostName[0] = '\0'; 2596 cls.localServers[i].mapName[0] = '\0'; 2597 cls.localServers[i].maxClients = 0; 2598 cls.localServers[i].maxPing = 0; 2599 cls.localServers[i].minPing = 0; 2600 cls.localServers[i].ping = -1; 2601 cls.localServers[i].game[0] = '\0'; 2602 cls.localServers[i].gameType = 0; 2603 cls.localServers[i].netType = from.type; 2604 cls.localServers[i].punkbuster = 0; 2605 2606 Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING ); 2607 if (strlen(info)) { 2608 if (info[strlen(info)-1] != '\n') { 2609 strncat(info, "\n", sizeof(info)); 2610 } 2611 Com_Printf( "%s: %s", NET_AdrToString( from ), info ); 2612 } 2613 } 2614 2615 /* 2616 =================== 2617 CL_GetServerStatus 2618 =================== 2619 */ 2620 serverStatus_t *CL_GetServerStatus( netadr_t from ) { 2621 serverStatus_t *serverStatus; 2622 int i, oldest, oldestTime; 2623 2624 serverStatus = NULL; 2625 for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { 2626 if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) { 2627 return &cl_serverStatusList[i]; 2628 } 2629 } 2630 for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { 2631 if ( cl_serverStatusList[i].retrieved ) { 2632 return &cl_serverStatusList[i]; 2633 } 2634 } 2635 oldest = -1; 2636 oldestTime = 0; 2637 for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { 2638 if (oldest == -1 || cl_serverStatusList[i].startTime < oldestTime) { 2639 oldest = i; 2640 oldestTime = cl_serverStatusList[i].startTime; 2641 } 2642 } 2643 if (oldest != -1) { 2644 return &cl_serverStatusList[oldest]; 2645 } 2646 serverStatusCount++; 2647 return &cl_serverStatusList[serverStatusCount & (MAX_SERVERSTATUSREQUESTS-1)]; 2648 } 2649 2650 /* 2651 =================== 2652 CL_ServerStatus 2653 =================== 2654 */ 2655 int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ) { 2656 int i; 2657 netadr_t to; 2658 serverStatus_t *serverStatus; 2659 2660 // if no server address then reset all server status requests 2661 if ( !serverAddress ) { 2662 for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { 2663 cl_serverStatusList[i].address.port = 0; 2664 cl_serverStatusList[i].retrieved = qtrue; 2665 } 2666 return qfalse; 2667 } 2668 // get the address 2669 if ( !NET_StringToAdr( serverAddress, &to ) ) { 2670 return qfalse; 2671 } 2672 serverStatus = CL_GetServerStatus( to ); 2673 // if no server status string then reset the server status request for this address 2674 if ( !serverStatusString ) { 2675 serverStatus->retrieved = qtrue; 2676 return qfalse; 2677 } 2678 2679 // if this server status request has the same address 2680 if ( NET_CompareAdr( to, serverStatus->address) ) { 2681 // if we recieved an response for this server status request 2682 if (!serverStatus->pending) { 2683 Q_strncpyz(serverStatusString, serverStatus->string, maxLen); 2684 serverStatus->retrieved = qtrue; 2685 serverStatus->startTime = 0; 2686 return qtrue; 2687 } 2688 // resend the request regularly 2689 else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) { 2690 serverStatus->print = qfalse; 2691 serverStatus->pending = qtrue; 2692 serverStatus->retrieved = qfalse; 2693 serverStatus->time = 0; 2694 serverStatus->startTime = Com_Milliseconds(); 2695 NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); 2696 return qfalse; 2697 } 2698 } 2699 // if retrieved 2700 else if ( serverStatus->retrieved ) { 2701 serverStatus->address = to; 2702 serverStatus->print = qfalse; 2703 serverStatus->pending = qtrue; 2704 serverStatus->retrieved = qfalse; 2705 serverStatus->startTime = Com_Milliseconds(); 2706 serverStatus->time = 0; 2707 NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); 2708 return qfalse; 2709 } 2710 return qfalse; 2711 } 2712 2713 /* 2714 =================== 2715 CL_ServerStatusResponse 2716 =================== 2717 */ 2718 void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) { 2719 char *s; 2720 char info[MAX_INFO_STRING]; 2721 int i, l, score, ping; 2722 int len; 2723 serverStatus_t *serverStatus; 2724 2725 serverStatus = NULL; 2726 for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) { 2727 if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) { 2728 serverStatus = &cl_serverStatusList[i]; 2729 break; 2730 } 2731 } 2732 // if we didn't request this server status 2733 if (!serverStatus) { 2734 return; 2735 } 2736 2737 s = MSG_ReadStringLine( msg ); 2738 2739 len = 0; 2740 Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "%s", s); 2741 2742 if (serverStatus->print) { 2743 Com_Printf("Server settings:\n"); 2744 // print cvars 2745 while (*s) { 2746 for (i = 0; i < 2 && *s; i++) { 2747 if (*s == '\\') 2748 s++; 2749 l = 0; 2750 while (*s) { 2751 info[l++] = *s; 2752 if (l >= MAX_INFO_STRING-1) 2753 break; 2754 s++; 2755 if (*s == '\\') { 2756 break; 2757 } 2758 } 2759 info[l] = '\0'; 2760 if (i) { 2761 Com_Printf("%s\n", info); 2762 } 2763 else { 2764 Com_Printf("%-24s", info); 2765 } 2766 } 2767 } 2768 } 2769 2770 len = strlen(serverStatus->string); 2771 Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\"); 2772 2773 if (serverStatus->print) { 2774 Com_Printf("\nPlayers:\n"); 2775 Com_Printf("num: score: ping: name:\n"); 2776 } 2777 for (i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++) { 2778 2779 len = strlen(serverStatus->string); 2780 Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\%s", s); 2781 2782 if (serverStatus->print) { 2783 score = ping = 0; 2784 sscanf(s, "%d %d", &score, &ping); 2785 s = strchr(s, ' '); 2786 if (s) 2787 s = strchr(s+1, ' '); 2788 if (s) 2789 s++; 2790 else 2791 s = "unknown"; 2792 Com_Printf("%-2d %-3d %-3d %s\n", i, score, ping, s ); 2793 } 2794 } 2795 len = strlen(serverStatus->string); 2796 Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\"); 2797 2798 serverStatus->time = Com_Milliseconds(); 2799 serverStatus->address = from; 2800 serverStatus->pending = qfalse; 2801 if (serverStatus->print) { 2802 serverStatus->retrieved = qtrue; 2803 } 2804 } 2805 2806 /* 2807 ================== 2808 CL_LocalServers_f 2809 ================== 2810 */ 2811 void CL_LocalServers_f( void ) { 2812 char *message; 2813 int i, j; 2814 netadr_t to; 2815 2816 Com_Printf( "Scanning for servers on the local network...\n"); 2817 2818 // reset the list, waiting for response 2819 cls.numlocalservers = 0; 2820 cls.pingUpdateSource = AS_LOCAL; 2821 2822 for (i = 0; i < MAX_OTHER_SERVERS; i++) { 2823 qboolean b = cls.localServers[i].visible; 2824 Com_Memset(&cls.localServers[i], 0, sizeof(cls.localServers[i])); 2825 cls.localServers[i].visible = b; 2826 } 2827 Com_Memset( &to, 0, sizeof( to ) ); 2828 2829 // The 'xxx' in the message is a challenge that will be echoed back 2830 // by the server. We don't care about that here, but master servers 2831 // can use that to prevent spoofed server responses from invalid ip 2832 message = "\377\377\377\377getinfo xxx"; 2833 2834 // send each message twice in case one is dropped 2835 for ( i = 0 ; i < 2 ; i++ ) { 2836 // send a broadcast packet on each server port 2837 // we support multiple server ports so a single machine 2838 // can nicely run multiple servers 2839 for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) { 2840 to.port = BigShort( (short)(PORT_SERVER + j) ); 2841 2842 to.type = NA_BROADCAST; 2843 NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); 2844 2845 to.type = NA_BROADCAST_IPX; 2846 NET_SendPacket( NS_CLIENT, strlen( message ), message, to ); 2847 } 2848 } 2849 } 2850 2851 /* 2852 ================== 2853 CL_GlobalServers_f 2854 ================== 2855 */ 2856 void CL_GlobalServers_f( void ) { 2857 netadr_t to; 2858 int i; 2859 int count; 2860 char *buffptr; 2861 char command[1024]; 2862 2863 if ( Cmd_Argc() < 3) { 2864 Com_Printf( "usage: globalservers <master# 0-1> <protocol> [keywords]\n"); 2865 return; 2866 } 2867 2868 cls.masterNum = atoi( Cmd_Argv(1) ); 2869 2870 Com_Printf( "Requesting servers from the master...\n"); 2871 2872 // reset the list, waiting for response 2873 // -1 is used to distinguish a "no response" 2874 2875 if( cls.masterNum == 1 ) { 2876 NET_StringToAdr( MASTER_SERVER_NAME, &to ); 2877 cls.nummplayerservers = -1; 2878 cls.pingUpdateSource = AS_MPLAYER; 2879 } 2880 else { 2881 NET_StringToAdr( MASTER_SERVER_NAME, &to ); 2882 cls.numglobalservers = -1; 2883 cls.pingUpdateSource = AS_GLOBAL; 2884 } 2885 to.type = NA_IP; 2886 to.port = BigShort(PORT_MASTER); 2887 2888 sprintf( command, "getservers %s", Cmd_Argv(2) ); 2889 2890 // tack on keywords 2891 buffptr = command + strlen( command ); 2892 count = Cmd_Argc(); 2893 for (i=3; i<count; i++) 2894 buffptr += sprintf( buffptr, " %s", Cmd_Argv(i) ); 2895 2896 // if we are a demo, automatically add a "demo" keyword 2897 if ( Cvar_VariableValue( "fs_restrict" ) ) { 2898 buffptr += sprintf( buffptr, " demo" ); 2899 } 2900 2901 NET_OutOfBandPrint( NS_SERVER, to, command ); 2902 } 2903 2904 2905 /* 2906 ================== 2907 CL_GetPing 2908 ================== 2909 */ 2910 void CL_GetPing( int n, char *buf, int buflen, int *pingtime ) 2911 { 2912 const char *str; 2913 int time; 2914 int maxPing; 2915 2916 if (!cl_pinglist[n].adr.port) 2917 { 2918 // empty slot 2919 buf[0] = '\0'; 2920 *pingtime = 0; 2921 return; 2922 } 2923 2924 str = NET_AdrToString( cl_pinglist[n].adr ); 2925 Q_strncpyz( buf, str, buflen ); 2926 2927 time = cl_pinglist[n].time; 2928 if (!time) 2929 { 2930 // check for timeout 2931 time = cls.realtime - cl_pinglist[n].start; 2932 maxPing = Cvar_VariableIntegerValue( "cl_maxPing" ); 2933 if( maxPing < 100 ) { 2934 maxPing = 100; 2935 } 2936 if (time < maxPing) 2937 { 2938 // not timed out yet 2939 time = 0; 2940 } 2941 } 2942 2943 CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time); 2944 2945 *pingtime = time; 2946 } 2947 2948 /* 2949 ================== 2950 CL_UpdateServerInfo 2951 ================== 2952 */ 2953 void CL_UpdateServerInfo( int n ) 2954 { 2955 if (!cl_pinglist[n].adr.port) 2956 { 2957 return; 2958 } 2959 2960 CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time ); 2961 } 2962 2963 /* 2964 ================== 2965 CL_GetPingInfo 2966 ================== 2967 */ 2968 void CL_GetPingInfo( int n, char *buf, int buflen ) 2969 { 2970 if (!cl_pinglist[n].adr.port) 2971 { 2972 // empty slot 2973 if (buflen) 2974 buf[0] = '\0'; 2975 return; 2976 } 2977 2978 Q_strncpyz( buf, cl_pinglist[n].info, buflen ); 2979 } 2980 2981 /* 2982 ================== 2983 CL_ClearPing 2984 ================== 2985 */ 2986 void CL_ClearPing( int n ) 2987 { 2988 if (n < 0 || n >= MAX_PINGREQUESTS) 2989 return; 2990 2991 cl_pinglist[n].adr.port = 0; 2992 } 2993 2994 /* 2995 ================== 2996 CL_GetPingQueueCount 2997 ================== 2998 */ 2999 int CL_GetPingQueueCount( void ) 3000 { 3001 int i; 3002 int count; 3003 ping_t* pingptr; 3004 3005 count = 0; 3006 pingptr = cl_pinglist; 3007 3008 for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ ) { 3009 if (pingptr->adr.port) { 3010 count++; 3011 } 3012 } 3013 3014 return (count); 3015 } 3016 3017 /* 3018 ================== 3019 CL_GetFreePing 3020 ================== 3021 */ 3022 ping_t* CL_GetFreePing( void ) 3023 { 3024 ping_t* pingptr; 3025 ping_t* best; 3026 int oldest; 3027 int i; 3028 int time; 3029 3030 pingptr = cl_pinglist; 3031 for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ ) 3032 { 3033 // find free ping slot 3034 if (pingptr->adr.port) 3035 { 3036 if (!pingptr->time) 3037 { 3038 if (cls.realtime - pingptr->start < 500) 3039 { 3040 // still waiting for response 3041 continue; 3042 } 3043 } 3044 else if (pingptr->time < 500) 3045 { 3046 // results have not been queried 3047 continue; 3048 } 3049 } 3050 3051 // clear it 3052 pingptr->adr.port = 0; 3053 return (pingptr); 3054 } 3055 3056 // use oldest entry 3057 pingptr = cl_pinglist; 3058 best = cl_pinglist; 3059 oldest = INT_MIN; 3060 for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ ) 3061 { 3062 // scan for oldest 3063 time = cls.realtime - pingptr->start; 3064 if (time > oldest) 3065 { 3066 oldest = time; 3067 best = pingptr; 3068 } 3069 } 3070 3071 return (best); 3072 } 3073 3074 /* 3075 ================== 3076 CL_Ping_f 3077 ================== 3078 */ 3079 void CL_Ping_f( void ) { 3080 netadr_t to; 3081 ping_t* pingptr; 3082 char* server; 3083 3084 if ( Cmd_Argc() != 2 ) { 3085 Com_Printf( "usage: ping [server]\n"); 3086 return; 3087 } 3088 3089 Com_Memset( &to, 0, sizeof(netadr_t) ); 3090 3091 server = Cmd_Argv(1); 3092 3093 if ( !NET_StringToAdr( server, &to ) ) { 3094 return; 3095 } 3096 3097 pingptr = CL_GetFreePing(); 3098 3099 memcpy( &pingptr->adr, &to, sizeof (netadr_t) ); 3100 pingptr->start = cls.realtime; 3101 pingptr->time = 0; 3102 3103 CL_SetServerInfoByAddress(pingptr->adr, NULL, 0); 3104 3105 NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" ); 3106 } 3107 3108 /* 3109 ================== 3110 CL_UpdateVisiblePings_f 3111 ================== 3112 */ 3113 qboolean CL_UpdateVisiblePings_f(int source) { 3114 int slots, i; 3115 char buff[MAX_STRING_CHARS]; 3116 int pingTime; 3117 int max; 3118 qboolean status = qfalse; 3119 3120 if (source < 0 || source > AS_FAVORITES) { 3121 return qfalse; 3122 } 3123 3124 cls.pingUpdateSource = source; 3125 3126 slots = CL_GetPingQueueCount(); 3127 if (slots < MAX_PINGREQUESTS) { 3128 serverInfo_t *server = NULL; 3129 3130 max = (source == AS_GLOBAL) ? MAX_GLOBAL_SERVERS : MAX_OTHER_SERVERS; 3131 switch (source) { 3132 case AS_LOCAL : 3133 server = &cls.localServers[0]; 3134 max = cls.numlocalservers; 3135 break; 3136 case AS_MPLAYER : 3137 server = &cls.mplayerServers[0]; 3138 max = cls.nummplayerservers; 3139 break; 3140 case AS_GLOBAL : 3141 server = &cls.globalServers[0]; 3142 max = cls.numglobalservers; 3143 break; 3144 case AS_FAVORITES : 3145 server = &cls.favoriteServers[0]; 3146 max = cls.numfavoriteservers; 3147 break; 3148 } 3149 for (i = 0; i < max; i++) { 3150 if (server[i].visible) { 3151 if (server[i].ping == -1) { 3152 int j; 3153 3154 if (slots >= MAX_PINGREQUESTS) { 3155 break; 3156 } 3157 for (j = 0; j < MAX_PINGREQUESTS; j++) { 3158 if (!cl_pinglist[j].adr.port) { 3159 continue; 3160 } 3161 if (NET_CompareAdr( cl_pinglist[j].adr, server[i].adr)) { 3162 // already on the list 3163 break; 3164 } 3165 } 3166 if (j >= MAX_PINGREQUESTS) { 3167 status = qtrue; 3168 for (j = 0; j < MAX_PINGREQUESTS; j++) { 3169 if (!cl_pinglist[j].adr.port) { 3170 break; 3171 } 3172 } 3173 memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t)); 3174 cl_pinglist[j].start = cls.realtime; 3175 cl_pinglist[j].time = 0; 3176 NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" ); 3177 slots++; 3178 } 3179 } 3180 // if the server has a ping higher than cl_maxPing or 3181 // the ping packet got lost 3182 else if (server[i].ping == 0) { 3183 // if we are updating global servers 3184 if (source == AS_GLOBAL) { 3185 // 3186 if ( cls.numGlobalServerAddresses > 0 ) { 3187 // overwrite this server with one from the additional global servers 3188 cls.numGlobalServerAddresses--; 3189 CL_InitServerInfo(&server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses]); 3190 // NOTE: the server[i].visible flag stays untouched 3191 } 3192 } 3193 } 3194 } 3195 } 3196 } 3197 3198 if (slots) { 3199 status = qtrue; 3200 } 3201 for (i = 0; i < MAX_PINGREQUESTS; i++) { 3202 if (!cl_pinglist[i].adr.port) { 3203 continue; 3204 } 3205 CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime ); 3206 if (pingTime != 0) { 3207 CL_ClearPing(i); 3208 status = qtrue; 3209 } 3210 } 3211 3212 return status; 3213 } 3214 3215 /* 3216 ================== 3217 CL_ServerStatus_f 3218 ================== 3219 */ 3220 void CL_ServerStatus_f(void) { 3221 netadr_t to; 3222 char *server; 3223 serverStatus_t *serverStatus; 3224 3225 Com_Memset( &to, 0, sizeof(netadr_t) ); 3226 3227 if ( Cmd_Argc() != 2 ) { 3228 if ( cls.state != CA_ACTIVE || clc.demoplaying ) { 3229 Com_Printf ("Not connected to a server.\n"); 3230 Com_Printf( "Usage: serverstatus [server]\n"); 3231 return; 3232 } 3233 server = cls.servername; 3234 } 3235 else { 3236 server = Cmd_Argv(1); 3237 } 3238 3239 if ( !NET_StringToAdr( server, &to ) ) { 3240 return; 3241 } 3242 3243 NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" ); 3244 3245 serverStatus = CL_GetServerStatus( to ); 3246 serverStatus->address = to; 3247 serverStatus->print = qtrue; 3248 serverStatus->pending = qtrue; 3249 } 3250 3251 /* 3252 ================== 3253 CL_ShowIP_f 3254 ================== 3255 */ 3256 void CL_ShowIP_f(void) { 3257 Sys_ShowIP(); 3258 } 3259 3260 /* 3261 ================= 3262 bool CL_CDKeyValidate 3263 ================= 3264 */ 3265 qboolean CL_CDKeyValidate( const char *key, const char *checksum ) { 3266 char ch; 3267 byte sum; 3268 char chs[3]; 3269 int i, len; 3270 3271 len = strlen(key); 3272 if( len != CDKEY_LEN ) { 3273 return qfalse; 3274 } 3275 3276 if( checksum && strlen( checksum ) != CDCHKSUM_LEN ) { 3277 return qfalse; 3278 } 3279 3280 sum = 0; 3281 // for loop gets rid of conditional assignment warning 3282 for (i = 0; i < len; i++) { 3283 ch = *key++; 3284 if (ch>='a' && ch<='z') { 3285 ch -= 32; 3286 } 3287 switch( ch ) { 3288 case '2': 3289 case '3': 3290 case '7': 3291 case 'A': 3292 case 'B': 3293 case 'C': 3294 case 'D': 3295 case 'G': 3296 case 'H': 3297 case 'J': 3298 case 'L': 3299 case 'P': 3300 case 'R': 3301 case 'S': 3302 case 'T': 3303 case 'W': 3304 sum += ch; 3305 continue; 3306 default: 3307 return qfalse; 3308 } 3309 } 3310 3311 sprintf(chs, "%02x", sum); 3312 3313 if (checksum && !Q_stricmp(chs, checksum)) { 3314 return qtrue; 3315 } 3316 3317 if (!checksum) { 3318 return qtrue; 3319 } 3320 3321 return qfalse; 3322 } 3323 3324