Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

cl_main.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 // cl_main.c  -- client main loop
00023 
00024 #include "client.h"
00025 #include <limits.h>
00026 
00027 cvar_t  *cl_nodelta;
00028 cvar_t  *cl_debugMove;
00029 
00030 cvar_t  *cl_noprint;
00031 cvar_t  *cl_motd;
00032 
00033 cvar_t  *rcon_client_password;
00034 cvar_t  *rconAddress;
00035 
00036 cvar_t  *cl_timeout;
00037 cvar_t  *cl_maxpackets;
00038 cvar_t  *cl_packetdup;
00039 cvar_t  *cl_timeNudge;
00040 cvar_t  *cl_showTimeDelta;
00041 cvar_t  *cl_freezeDemo;
00042 
00043 cvar_t  *cl_shownet;
00044 cvar_t  *cl_showSend;
00045 cvar_t  *cl_timedemo;
00046 cvar_t  *cl_avidemo;
00047 cvar_t  *cl_forceavidemo;
00048 
00049 cvar_t  *cl_freelook;
00050 cvar_t  *cl_sensitivity;
00051 
00052 cvar_t  *cl_mouseAccel;
00053 cvar_t  *cl_showMouseRate;
00054 
00055 cvar_t  *m_pitch;
00056 cvar_t  *m_yaw;
00057 cvar_t  *m_forward;
00058 cvar_t  *m_side;
00059 cvar_t  *m_filter;
00060 
00061 cvar_t  *cl_activeAction;
00062 
00063 cvar_t  *cl_motdString;
00064 
00065 cvar_t  *cl_allowDownload;
00066 cvar_t  *cl_conXOffset;
00067 cvar_t  *cl_inGameVideo;
00068 
00069 cvar_t  *cl_serverStatusResendTime;
00070 cvar_t  *cl_trn;
00071 
00072 clientActive_t      cl;
00073 clientConnection_t  clc;
00074 clientStatic_t      cls;
00075 vm_t                *cgvm;
00076 
00077 // Structure containing functions exported from refresh DLL
00078 refexport_t re;
00079 
00080 ping_t  cl_pinglist[MAX_PINGREQUESTS];
00081 
00082 typedef struct serverStatus_s
00083 {
00084     char string[BIG_INFO_STRING];
00085     netadr_t address;
00086     int time, startTime;
00087     qboolean pending;
00088     qboolean print;
00089     qboolean retrieved;
00090 } serverStatus_t;
00091 
00092 serverStatus_t cl_serverStatusList[MAX_SERVERSTATUSREQUESTS];
00093 int serverStatusCount;
00094 
00095 #if defined __USEA3D && defined __A3D_GEOM
00096     void hA3Dg_ExportRenderGeom (refexport_t *incoming_re);
00097 #endif
00098 
00099 extern void SV_BotFrame( int time );
00100 void CL_CheckForResend( void );
00101 void CL_ShowIP_f(void);
00102 void CL_ServerStatus_f(void);
00103 void CL_ServerStatusResponse( netadr_t from, msg_t *msg );
00104 
00105 /*
00106 ===============
00107 CL_CDDialog
00108 
00109 Called by Com_Error when a cd is needed
00110 ===============
00111 */
00112 void CL_CDDialog( void ) {
00113     cls.cddialog = qtrue;   // start it next frame
00114 }
00115 
00116 
00117 /*
00118 =======================================================================
00119 
00120 CLIENT RELIABLE COMMAND COMMUNICATION
00121 
00122 =======================================================================
00123 */
00124 
00125 /*
00126 ======================
00127 CL_AddReliableCommand
00128 
00129 The given command will be transmitted to the server, and is gauranteed to
00130 not have future usercmd_t executed before it is executed
00131 ======================
00132 */
00133 void CL_AddReliableCommand( const char *cmd ) {
00134     int     index;
00135 
00136     // if we would be losing an old command that hasn't been acknowledged,
00137     // we must drop the connection
00138     if ( clc.reliableSequence - clc.reliableAcknowledge > MAX_RELIABLE_COMMANDS ) {
00139         Com_Error( ERR_DROP, "Client command overflow" );
00140     }
00141     clc.reliableSequence++;
00142     index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
00143     Q_strncpyz( clc.reliableCommands[ index ], cmd, sizeof( clc.reliableCommands[ index ] ) );
00144 }
00145 
00146 /*
00147 ======================
00148 CL_ChangeReliableCommand
00149 ======================
00150 */
00151 void CL_ChangeReliableCommand( void ) {
00152     int r, index, l;
00153 
00154     r = clc.reliableSequence - (random() * 5);
00155     index = clc.reliableSequence & ( MAX_RELIABLE_COMMANDS - 1 );
00156     l = strlen(clc.reliableCommands[ index ]);
00157     if ( l >= MAX_STRING_CHARS - 1 ) {
00158         l = MAX_STRING_CHARS - 2;
00159     }
00160     clc.reliableCommands[ index ][ l ] = '\n';
00161     clc.reliableCommands[ index ][ l+1 ] = '\0';
00162 }
00163 
00164 /*
00165 =======================================================================
00166 
00167 CLIENT SIDE DEMO RECORDING
00168 
00169 =======================================================================
00170 */
00171 
00172 /*
00173 ====================
00174 CL_WriteDemoMessage
00175 
00176 Dumps the current net message, prefixed by the length
00177 ====================
00178 */
00179 void CL_WriteDemoMessage ( msg_t *msg, int headerBytes ) {
00180     int     len, swlen;
00181 
00182     // write the packet sequence
00183     len = clc.serverMessageSequence;
00184     swlen = LittleLong( len );
00185     FS_Write (&swlen, 4, clc.demofile);
00186 
00187     // skip the packet sequencing information
00188     len = msg->cursize - headerBytes;
00189     swlen = LittleLong(len);
00190     FS_Write (&swlen, 4, clc.demofile);
00191     FS_Write ( msg->data + headerBytes, len, clc.demofile );
00192 }
00193 
00194 
00195 /*
00196 ====================
00197 CL_StopRecording_f
00198 
00199 stop recording a demo
00200 ====================
00201 */
00202 void CL_StopRecord_f( void ) {
00203     int     len;
00204 
00205     if ( !clc.demorecording ) {
00206         Com_Printf ("Not recording a demo.\n");
00207         return;
00208     }
00209 
00210     // finish up
00211     len = -1;
00212     FS_Write (&len, 4, clc.demofile);
00213     FS_Write (&len, 4, clc.demofile);
00214     FS_FCloseFile (clc.demofile);
00215     clc.demofile = 0;
00216     clc.demorecording = qfalse;
00217     clc.spDemoRecording = qfalse;
00218     Com_Printf ("Stopped demo.\n");
00219 }
00220 
00221 /* 
00222 ================== 
00223 CL_DemoFilename
00224 ================== 
00225 */  
00226 void CL_DemoFilename( int number, char *fileName ) {
00227     int     a,b,c,d;
00228 
00229     if ( number < 0 || number > 9999 ) {
00230         Com_sprintf( fileName, MAX_OSPATH, "demo9999.tga" );
00231         return;
00232     }
00233 
00234     a = number / 1000;
00235     number -= a*1000;
00236     b = number / 100;
00237     number -= b*100;
00238     c = number / 10;
00239     number -= c*10;
00240     d = number;
00241 
00242     Com_sprintf( fileName, MAX_OSPATH, "demo%i%i%i%i"
00243         , a, b, c, d );
00244 }
00245 
00246 /*
00247 ====================
00248 CL_Record_f
00249 
00250 record <demoname>
00251 
00252 Begins recording a demo from the current position
00253 ====================
00254 */
00255 static char     demoName[MAX_QPATH];    // compiler bug workaround
00256 void CL_Record_f( void ) {
00257     char        name[MAX_OSPATH];
00258     byte        bufData[MAX_MSGLEN];
00259     msg_t   buf;
00260     int         i;
00261     int         len;
00262     entityState_t   *ent;
00263     entityState_t   nullstate;
00264     char        *s;
00265 
00266     if ( Cmd_Argc() > 2 ) {
00267         Com_Printf ("record <demoname>\n");
00268         return;
00269     }
00270 
00271     if ( clc.demorecording ) {
00272         if (!clc.spDemoRecording) {
00273             Com_Printf ("Already recording.\n");
00274         }
00275         return;
00276     }
00277 
00278     if ( cls.state != CA_ACTIVE ) {
00279         Com_Printf ("You must be in a level to record.\n");
00280         return;
00281     }
00282 
00283   // sync 0 doesn't prevent recording, so not forcing it off .. everyone does g_sync 1 ; record ; g_sync 0 ..
00284     if ( !Cvar_VariableValue( "g_synchronousClients" ) ) {
00285         Com_Printf (S_COLOR_YELLOW "WARNING: You should set 'g_synchronousClients 1' for smoother demo recording\n");
00286     }
00287 
00288     if ( Cmd_Argc() == 2 ) {
00289         s = Cmd_Argv(1);
00290         Q_strncpyz( demoName, s, sizeof( demoName ) );
00291         Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION );
00292     } else {
00293         int     number;
00294 
00295         // scan for a free demo name
00296         for ( number = 0 ; number <= 9999 ; number++ ) {
00297             CL_DemoFilename( number, demoName );
00298             Com_sprintf (name, sizeof(name), "demos/%s.dm_%d", demoName, PROTOCOL_VERSION );
00299 
00300             len = FS_ReadFile( name, NULL );
00301             if ( len <= 0 ) {
00302                 break;  // file doesn't exist
00303             }
00304         }
00305     }
00306 
00307     // open the demo file
00308 
00309     Com_Printf ("recording to %s.\n", name);
00310     clc.demofile = FS_FOpenFileWrite( name );
00311     if ( !clc.demofile ) {
00312         Com_Printf ("ERROR: couldn't open.\n");
00313         return;
00314     }
00315     clc.demorecording = qtrue;
00316     if (Cvar_VariableValue("ui_recordSPDemo")) {
00317       clc.spDemoRecording = qtrue;
00318     } else {
00319       clc.spDemoRecording = qfalse;
00320     }
00321 
00322 
00323     Q_strncpyz( clc.demoName, demoName, sizeof( clc.demoName ) );
00324 
00325     // don't start saving messages until a non-delta compressed message is received
00326     clc.demowaiting = qtrue;
00327 
00328     // write out the gamestate message
00329     MSG_Init (&buf, bufData, sizeof(bufData));
00330     MSG_Bitstream(&buf);
00331 
00332     // NOTE, MRE: all server->client messages now acknowledge
00333     MSG_WriteLong( &buf, clc.reliableSequence );
00334 
00335     MSG_WriteByte (&buf, svc_gamestate);
00336     MSG_WriteLong (&buf, clc.serverCommandSequence );
00337 
00338     // configstrings
00339     for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
00340         if ( !cl.gameState.stringOffsets[i] ) {
00341             continue;
00342         }
00343         s = cl.gameState.stringData + cl.gameState.stringOffsets[i];
00344         MSG_WriteByte (&buf, svc_configstring);
00345         MSG_WriteShort (&buf, i);
00346         MSG_WriteBigString (&buf, s);
00347     }
00348 
00349     // baselines
00350     Com_Memset (&nullstate, 0, sizeof(nullstate));
00351     for ( i = 0; i < MAX_GENTITIES ; i++ ) {
00352         ent = &cl.entityBaselines[i];
00353         if ( !ent->number ) {
00354             continue;
00355         }
00356         MSG_WriteByte (&buf, svc_baseline);     
00357         MSG_WriteDeltaEntity (&buf, &nullstate, ent, qtrue );
00358     }
00359 
00360     MSG_WriteByte( &buf, svc_EOF );
00361     
00362     // finished writing the gamestate stuff
00363 
00364     // write the client num
00365     MSG_WriteLong(&buf, clc.clientNum);
00366     // write the checksum feed
00367     MSG_WriteLong(&buf, clc.checksumFeed);
00368 
00369     // finished writing the client packet
00370     MSG_WriteByte( &buf, svc_EOF );
00371 
00372     // write it to the demo file
00373     len = LittleLong( clc.serverMessageSequence - 1 );
00374     FS_Write (&len, 4, clc.demofile);
00375 
00376     len = LittleLong (buf.cursize);
00377     FS_Write (&len, 4, clc.demofile);
00378     FS_Write (buf.data, buf.cursize, clc.demofile);
00379 
00380     // the rest of the demo file will be copied from net messages
00381 }
00382 
00383 /*
00384 =======================================================================
00385 
00386 CLIENT SIDE DEMO PLAYBACK
00387 
00388 =======================================================================
00389 */
00390 
00391 /*
00392 =================
00393 CL_DemoCompleted
00394 =================
00395 */
00396 void CL_DemoCompleted( void ) {
00397     if (cl_timedemo && cl_timedemo->integer) {
00398         int time;
00399         
00400         time = Sys_Milliseconds() - clc.timeDemoStart;
00401         if ( time > 0 ) {
00402             Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", clc.timeDemoFrames,
00403             time/1000.0, clc.timeDemoFrames*1000.0 / time);
00404         }
00405     }
00406 
00407     CL_Disconnect( qtrue );
00408     CL_NextDemo();
00409 }
00410 
00411 /*
00412 =================
00413 CL_ReadDemoMessage
00414 =================
00415 */
00416 void CL_ReadDemoMessage( void ) {
00417     int         r;
00418     msg_t       buf;
00419     byte        bufData[ MAX_MSGLEN ];
00420     int         s;
00421 
00422     if ( !clc.demofile ) {
00423         CL_DemoCompleted ();
00424         return;
00425     }
00426 
00427     // get the sequence number
00428     r = FS_Read( &s, 4, clc.demofile);
00429     if ( r != 4 ) {
00430         CL_DemoCompleted ();
00431         return;
00432     }
00433     clc.serverMessageSequence = LittleLong( s );
00434 
00435     // init the message
00436     MSG_Init( &buf, bufData, sizeof( bufData ) );
00437 
00438     // get the length
00439     r = FS_Read (&buf.cursize, 4, clc.demofile);
00440     if ( r != 4 ) {
00441         CL_DemoCompleted ();
00442         return;
00443     }
00444     buf.cursize = LittleLong( buf.cursize );
00445     if ( buf.cursize == -1 ) {
00446         CL_DemoCompleted ();
00447         return;
00448     }
00449     if ( buf.cursize > buf.maxsize ) {
00450         Com_Error (ERR_DROP, "CL_ReadDemoMessage: demoMsglen > MAX_MSGLEN");
00451     }
00452     r = FS_Read( buf.data, buf.cursize, clc.demofile );
00453     if ( r != buf.cursize ) {
00454         Com_Printf( "Demo file was truncated.\n");
00455         CL_DemoCompleted ();
00456         return;
00457     }
00458 
00459     clc.lastPacketTime = cls.realtime;
00460     buf.readcount = 0;
00461     CL_ParseServerMessage( &buf );
00462 }
00463 
00464 /*
00465 ====================
00466 CL_WalkDemoExt
00467 ====================
00468 */
00469 static void CL_WalkDemoExt(char *arg, char *name, int *demofile)
00470 {
00471     int i = 0;
00472     *demofile = 0;
00473     while(demo_protocols[i])
00474     {
00475         Com_sprintf (name, MAX_OSPATH, "demos/%s.dm_%d", arg, demo_protocols[i]);
00476         FS_FOpenFileRead( name, demofile, qtrue );
00477         if (*demofile)
00478         {
00479             Com_Printf("Demo file: %s\n", name);
00480             break;
00481         }
00482         else
00483             Com_Printf("Not found: %s\n", name);
00484         i++;
00485     }
00486 }
00487 
00488 /*
00489 ====================
00490 CL_PlayDemo_f
00491 
00492 demo <demoname>
00493 
00494 ====================
00495 */
00496 void CL_PlayDemo_f( void ) {
00497     char        name[MAX_OSPATH];
00498     char        *arg, *ext_test;
00499     int         protocol, i;
00500     char        retry[MAX_OSPATH];
00501 
00502     if (Cmd_Argc() != 2) {
00503         Com_Printf ("playdemo <demoname>\n");
00504         return;
00505     }
00506 
00507     // make sure a local server is killed
00508     Cvar_Set( "sv_killserver", "1" );
00509 
00510     CL_Disconnect( qtrue );
00511 
00512     // open the demo file
00513     arg = Cmd_Argv(1);
00514     
00515     // check for an extension .dm_?? (?? is protocol)
00516     ext_test = arg + strlen(arg) - 6;
00517     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] == '_'))
00518     {
00519         protocol = atoi(ext_test+4);
00520         i=0;
00521         while(demo_protocols[i])
00522         {
00523             if (demo_protocols[i] == protocol)
00524                 break;
00525             i++;
00526         }
00527         if (demo_protocols[i])
00528         {
00529             Com_sprintf (name, sizeof(name), "demos/%s", arg);
00530             FS_FOpenFileRead( name, &clc.demofile, qtrue );
00531         } else {
00532             Com_Printf("Protocol %d not supported for demos\n", protocol);
00533             Q_strncpyz(retry, arg, sizeof(retry));
00534             retry[strlen(retry)-6] = 0;
00535             CL_WalkDemoExt( retry, name, &clc.demofile );
00536         }
00537     } else {
00538         CL_WalkDemoExt( arg, name, &clc.demofile );
00539     }
00540     
00541     if (!clc.demofile) {
00542         Com_Error( ERR_DROP, "couldn't open %s", name);
00543         return;
00544     }
00545     Q_strncpyz( clc.demoName, Cmd_Argv(1), sizeof( clc.demoName ) );
00546 
00547     Con_Close();
00548 
00549     cls.state = CA_CONNECTED;
00550     clc.demoplaying = qtrue;
00551     Q_strncpyz( cls.servername, Cmd_Argv(1), sizeof( cls.servername ) );
00552 
00553     // read demo messages until connected
00554     while ( cls.state >= CA_CONNECTED && cls.state < CA_PRIMED ) {
00555         CL_ReadDemoMessage();
00556     }
00557     // don't get the first snapshot this frame, to prevent the long
00558     // time from the gamestate load from messing causing a time skip
00559     clc.firstDemoFrameSkipped = qfalse;
00560 }
00561 
00562 
00563 /*
00564 ====================
00565 CL_StartDemoLoop
00566 
00567 Closing the main menu will restart the demo loop
00568 ====================
00569 */
00570 void CL_StartDemoLoop( void ) {
00571     // start the demo loop again
00572     Cbuf_AddText ("d1\n");
00573     cls.keyCatchers = 0;
00574 }
00575 
00576 /*
00577 ==================
00578 CL_NextDemo
00579 
00580 Called when a demo or cinematic finishes
00581 If the "nextdemo" cvar is set, that command will be issued
00582 ==================
00583 */
00584 void CL_NextDemo( void ) {
00585     char    v[MAX_STRING_CHARS];
00586 
00587     Q_strncpyz( v, Cvar_VariableString ("nextdemo"), sizeof(v) );
00588     v[MAX_STRING_CHARS-1] = 0;
00589     Com_DPrintf("CL_NextDemo: %s\n", v );
00590     if (!v[0]) {
00591         return;
00592     }
00593 
00594     Cvar_Set ("nextdemo","");
00595     Cbuf_AddText (v);
00596     Cbuf_AddText ("\n");
00597     Cbuf_Execute();
00598 }
00599 
00600 
00601 //======================================================================
00602 
00603 /*
00604 =====================
00605 CL_ShutdownAll
00606 =====================
00607 */
00608 void CL_ShutdownAll(void) {
00609 
00610     // clear sounds
00611     S_DisableSounds();
00612     // shutdown CGame
00613     CL_ShutdownCGame();
00614     // shutdown UI
00615     CL_ShutdownUI();
00616 
00617     // shutdown the renderer
00618     if ( re.Shutdown ) {
00619         re.Shutdown( qfalse );      // don't destroy window or context
00620     }
00621 
00622     cls.uiStarted = qfalse;
00623     cls.cgameStarted = qfalse;
00624     cls.rendererStarted = qfalse;
00625     cls.soundRegistered = qfalse;
00626 }
00627 
00628 /*
00629 =================
00630 CL_FlushMemory
00631 
00632 Called by CL_MapLoading, CL_Connect_f, CL_PlayDemo_f, and CL_ParseGamestate the only
00633 ways a client gets into a game
00634 Also called by Com_Error
00635 =================
00636 */
00637 void CL_FlushMemory( void ) {
00638 
00639     // shutdown all the client stuff
00640     CL_ShutdownAll();
00641 
00642     // if not running a server clear the whole hunk
00643     if ( !com_sv_running->integer ) {
00644         // clear the whole hunk
00645         Hunk_Clear();
00646         // clear collision map data
00647         CM_ClearMap();
00648     }
00649     else {
00650         // clear all the client data on the hunk
00651         Hunk_ClearToMark();
00652     }
00653 
00654     CL_StartHunkUsers();
00655 }
00656 
00657 /*
00658 =====================
00659 CL_MapLoading
00660 
00661 A local server is starting to load a map, so update the
00662 screen to let the user know about it, then dump all client
00663 memory on the hunk from cgame, ui, and renderer
00664 =====================
00665 */
00666 void CL_MapLoading( void ) {
00667     if ( !com_cl_running->integer ) {
00668         return;
00669     }
00670 
00671     Con_Close();
00672     cls.keyCatchers = 0;
00673 
00674     // if we are already connected to the local host, stay connected
00675     if ( cls.state >= CA_CONNECTED && !Q_stricmp( cls.servername, "localhost" ) ) {
00676         cls.state = CA_CONNECTED;       // so the connect screen is drawn
00677         Com_Memset( cls.updateInfoString, 0, sizeof( cls.updateInfoString ) );
00678         Com_Memset( clc.serverMessage, 0, sizeof( clc.serverMessage ) );
00679         Com_Memset( &cl.gameState, 0, sizeof( cl.gameState ) );
00680         clc.lastPacketSentTime = -9999;
00681         SCR_UpdateScreen();
00682     } else {
00683         // clear nextmap so the cinematic shutdown doesn't execute it
00684         Cvar_Set( "nextmap", "" );
00685         CL_Disconnect( qtrue );
00686         Q_strncpyz( cls.servername, "localhost", sizeof(cls.servername) );
00687         cls.state = CA_CHALLENGING;     // so the connect screen is drawn
00688         cls.keyCatchers = 0;
00689         SCR_UpdateScreen();
00690         clc.connectTime = -RETRANSMIT_TIMEOUT;
00691         NET_StringToAdr( cls.servername, &clc.serverAddress);
00692         // we don't need a challenge on the localhost
00693 
00694         CL_CheckForResend();
00695     }
00696 }
00697 
00698 /*
00699 =====================
00700 CL_ClearState
00701 
00702 Called before parsing a gamestate
00703 =====================
00704 */
00705 void CL_ClearState (void) {
00706 
00707 //  S_StopAllSounds();
00708 
00709     Com_Memset( &cl, 0, sizeof( cl ) );
00710 }
00711 
00712 
00713 /*
00714 =====================
00715 CL_Disconnect
00716 
00717 Called when a connection, demo, or cinematic is being terminated.
00718 Goes from a connected state to either a menu state or a console state
00719 Sends a disconnect message to the server
00720 This is also called on Com_Error and Com_Quit, so it shouldn't cause any errors
00721 =====================
00722 */
00723 void CL_Disconnect( qboolean showMainMenu ) {
00724     if ( !com_cl_running || !com_cl_running->integer ) {
00725         return;
00726     }
00727 
00728     // shutting down the client so enter full screen ui mode
00729     Cvar_Set("r_uiFullScreen", "1");
00730 
00731     if ( clc.demorecording ) {
00732         CL_StopRecord_f ();
00733     }
00734 
00735     if (clc.download) {
00736         FS_FCloseFile( clc.download );
00737         clc.download = 0;
00738     }
00739     *clc.downloadTempName = *clc.downloadName = 0;
00740     Cvar_Set( "cl_downloadName", "" );
00741 
00742     if ( clc.demofile ) {
00743         FS_FCloseFile( clc.demofile );
00744         clc.demofile = 0;
00745     }
00746 
00747     if ( uivm && showMainMenu ) {
00748         VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NONE );
00749     }
00750 
00751     SCR_StopCinematic ();
00752     S_ClearSoundBuffer();
00753 
00754     // send a disconnect message to the server
00755     // send it a few times in case one is dropped
00756     if ( cls.state >= CA_CONNECTED ) {
00757         CL_AddReliableCommand( "disconnect" );
00758         CL_WritePacket();
00759         CL_WritePacket();
00760         CL_WritePacket();
00761     }
00762     
00763     CL_ClearState ();
00764 
00765     // wipe the client connection
00766     Com_Memset( &clc, 0, sizeof( clc ) );
00767 
00768     cls.state = CA_DISCONNECTED;
00769 
00770     // allow cheats locally
00771     Cvar_Set( "sv_cheats", "1" );
00772 
00773     // not connected to a pure server anymore
00774     cl_connectedToPureServer = qfalse;
00775 }
00776 
00777 
00778 /*
00779 ===================
00780 CL_ForwardCommandToServer
00781 
00782 adds the current command line as a clientCommand
00783 things like godmode, noclip, etc, are commands directed to the server,
00784 so when they are typed in at the console, they will need to be forwarded.
00785 ===================
00786 */
00787 void CL_ForwardCommandToServer( const char *string ) {
00788     char    *cmd;
00789 
00790     cmd = Cmd_Argv(0);
00791 
00792     // ignore key up commands
00793     if ( cmd[0] == '-' ) {
00794         return;
00795     }
00796 
00797     if ( clc.demoplaying || cls.state < CA_CONNECTED || cmd[0] == '+' ) {
00798         Com_Printf ("Unknown command \"%s\"\n", cmd);
00799         return;
00800     }
00801 
00802     if ( Cmd_Argc() > 1 ) {
00803         CL_AddReliableCommand( string );
00804     } else {
00805         CL_AddReliableCommand( cmd );
00806     }
00807 }
00808 
00809 /*
00810 ===================
00811 CL_RequestMotd
00812 
00813 ===================
00814 */
00815 void CL_RequestMotd( void ) {
00816     char        info[MAX_INFO_STRING];
00817 
00818     if ( !cl_motd->integer ) {
00819         return;
00820     }
00821     Com_Printf( "Resolving %s\n", UPDATE_SERVER_NAME );
00822     if ( !NET_StringToAdr( UPDATE_SERVER_NAME, &cls.updateServer  ) ) {
00823         Com_Printf( "Couldn't resolve address\n" );
00824         return;
00825     }
00826     cls.updateServer.port = BigShort( PORT_UPDATE );
00827     Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", UPDATE_SERVER_NAME,
00828         cls.updateServer.ip[0], cls.updateServer.ip[1],
00829         cls.updateServer.ip[2], cls.updateServer.ip[3],
00830         BigShort( cls.updateServer.port ) );
00831     
00832     info[0] = 0;
00833   // NOTE TTimo xoring against Com_Milliseconds, otherwise we may not have a true randomization
00834   // only srand I could catch before here is tr_noise.c l:26 srand(1001)
00835   // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=382
00836   // NOTE: the Com_Milliseconds xoring only affects the lower 16-bit word,
00837   //   but I decided it was enough randomization
00838     Com_sprintf( cls.updateChallenge, sizeof( cls.updateChallenge ), "%i", ((rand() << 16) ^ rand()) ^ Com_Milliseconds());
00839 
00840     Info_SetValueForKey( info, "challenge", cls.updateChallenge );
00841     Info_SetValueForKey( info, "renderer", cls.glconfig.renderer_string );
00842     Info_SetValueForKey( info, "version", com_version->string );
00843 
00844     NET_OutOfBandPrint( NS_CLIENT, cls.updateServer, "getmotd \"%s\"\n", info );
00845 }
00846 
00847 /*
00848 ===================
00849 CL_RequestAuthorization
00850 
00851 Authorization server protocol
00852 -----------------------------
00853 
00854 All commands are text in Q3 out of band packets (leading 0xff 0xff 0xff 0xff).
00855 
00856 Whenever the client tries to get a challenge from the server it wants to
00857 connect to, it also blindly fires off a packet to the authorize server:
00858 
00859 getKeyAuthorize <challenge> <cdkey>
00860 
00861 cdkey may be "demo"
00862 
00863 
00864 #OLD The authorize server returns a:
00865 #OLD 
00866 #OLD keyAthorize <challenge> <accept | deny>
00867 #OLD 
00868 #OLD A client will be accepted if the cdkey is valid and it has not been used by any other IP
00869 #OLD address in the last 15 minutes.
00870 
00871 
00872 The server sends a:
00873 
00874 getIpAuthorize <challenge> <ip>
00875 
00876 The authorize server returns a:
00877 
00878 ipAuthorize <challenge> <accept | deny | demo | unknown >
00879 
00880 A client will be accepted if a valid cdkey was sent by that ip (only) in the last 15 minutes.
00881 If no response is received from the authorize server after two tries, the client will be let
00882 in anyway.
00883 ===================
00884 */
00885 void CL_RequestAuthorization( void ) {
00886     char    nums[64];
00887     int     i, j, l;
00888     cvar_t  *fs;
00889 
00890     if ( !cls.authorizeServer.port ) {
00891         Com_Printf( "Resolving %s\n", AUTHORIZE_SERVER_NAME );
00892         if ( !NET_StringToAdr( AUTHORIZE_SERVER_NAME, &cls.authorizeServer  ) ) {
00893             Com_Printf( "Couldn't resolve address\n" );
00894             return;
00895         }
00896 
00897         cls.authorizeServer.port = BigShort( PORT_AUTHORIZE );
00898         Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", AUTHORIZE_SERVER_NAME,
00899             cls.authorizeServer.ip[0], cls.authorizeServer.ip[1],
00900             cls.authorizeServer.ip[2], cls.authorizeServer.ip[3],
00901             BigShort( cls.authorizeServer.port ) );
00902     }
00903     if ( cls.authorizeServer.type == NA_BAD ) {
00904         return;
00905     }
00906 
00907     if ( Cvar_VariableValue( "fs_restrict" ) ) {
00908         Q_strncpyz( nums, "demota", sizeof( nums ) );
00909     } else {
00910         // only grab the alphanumeric values from the cdkey, to avoid any dashes or spaces
00911         j = 0;
00912         l = strlen( cl_cdkey );
00913         if ( l > 32 ) {
00914             l = 32;
00915         }
00916         for ( i = 0 ; i < l ; i++ ) {
00917             if ( ( cl_cdkey[i] >= '0' && cl_cdkey[i] <= '9' )
00918                 || ( cl_cdkey[i] >= 'a' && cl_cdkey[i] <= 'z' )
00919                 || ( cl_cdkey[i] >= 'A' && cl_cdkey[i] <= 'Z' )
00920                 ) {
00921                 nums[j] = cl_cdkey[i];
00922                 j++;
00923             }
00924         }
00925         nums[j] = 0;
00926     }
00927 
00928     fs = Cvar_Get ("cl_anonymous", "0", CVAR_INIT|CVAR_SYSTEMINFO );
00929 
00930     NET_OutOfBandPrint(NS_CLIENT, cls.authorizeServer, va("getKeyAuthorize %i %s", fs->integer, nums) );
00931 }
00932 
00933 /*
00934 ======================================================================
00935 
00936 CONSOLE COMMANDS
00937 
00938 ======================================================================
00939 */
00940 
00941 /*
00942 ==================
00943 CL_ForwardToServer_f
00944 ==================
00945 */
00946 void CL_ForwardToServer_f( void ) {
00947     if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
00948         Com_Printf ("Not connected to a server.\n");
00949         return;
00950     }
00951     
00952     // don't forward the first argument
00953     if ( Cmd_Argc() > 1 ) {
00954         CL_AddReliableCommand( Cmd_Args() );
00955     }
00956 }
00957 
00958 /*
00959 ==================
00960 CL_Setenv_f
00961 
00962 Mostly for controlling voodoo environment variables
00963 ==================
00964 */
00965 void CL_Setenv_f( void ) {
00966     int argc = Cmd_Argc();
00967 
00968     if ( argc > 2 ) {
00969         char buffer[1024];
00970         int i;
00971 
00972         strcpy( buffer, Cmd_Argv(1) );
00973         strcat( buffer, "=" );
00974 
00975         for ( i = 2; i < argc; i++ ) {
00976             strcat( buffer, Cmd_Argv( i ) );
00977             strcat( buffer, " " );
00978         }
00979 
00980         putenv( buffer );
00981     } else if ( argc == 2 ) {
00982         char *env = getenv( Cmd_Argv(1) );
00983 
00984         if ( env ) {
00985             Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
00986         } else {
00987             Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
00988         }
00989     }
00990 }
00991 
00992 
00993 /*
00994 ==================
00995 CL_Disconnect_f
00996 ==================
00997 */
00998 void CL_Disconnect_f( void ) {
00999     SCR_StopCinematic();
01000     Cvar_Set("ui_singlePlayerActive", "0");
01001     if ( cls.state != CA_DISCONNECTED && cls.state != CA_CINEMATIC ) {
01002         Com_Error (ERR_DISCONNECT, "Disconnected from server");
01003     }
01004 }
01005 
01006 
01007 /*
01008 ================
01009 CL_Reconnect_f
01010 
01011 ================
01012 */
01013 void CL_Reconnect_f( void ) {
01014     if ( !strlen( cls.servername ) || !strcmp( cls.servername, "localhost" ) ) {
01015         Com_Printf( "Can't reconnect to localhost.\n" );
01016         return;
01017     }
01018     Cvar_Set("ui_singlePlayerActive", "0");
01019     Cbuf_AddText( va("connect %s\n", cls.servername ) );
01020 }
01021 
01022 /*
01023 ================
01024 CL_Connect_f
01025 
01026 ================
01027 */
01028 void CL_Connect_f( void ) {
01029     char    *server;
01030 
01031     if ( Cmd_Argc() != 2 ) {
01032         Com_Printf( "usage: connect [server]\n");
01033         return; 
01034     }
01035 
01036     Cvar_Set("ui_singlePlayerActive", "0");
01037 
01038     // fire a message off to the motd server
01039     CL_RequestMotd();
01040 
01041     // clear any previous "server full" type messages
01042     clc.serverMessage[0] = 0;
01043 
01044     server = Cmd_Argv (1);
01045 
01046     if ( com_sv_running->integer && !strcmp( server, "localhost" ) ) {
01047         // if running a local server, kill it
01048         SV_Shutdown( "Server quit\n" );
01049     }
01050 
01051     // make sure a local server is killed
01052     Cvar_Set( "sv_killserver", "1" );
01053     SV_Frame( 0 );
01054 
01055     CL_Disconnect( qtrue );
01056     Con_Close();
01057 
01058     /* MrE: 2000-09-13: now called in CL_DownloadsComplete
01059     CL_FlushMemory( );
01060     */
01061 
01062     Q_strncpyz( cls.servername, server, sizeof(cls.servername) );
01063 
01064     if (!NET_StringToAdr( cls.servername, &clc.serverAddress) ) {
01065         Com_Printf ("Bad server address\n");
01066         cls.state = CA_DISCONNECTED;
01067         return;
01068     }
01069     if (clc.serverAddress.port == 0) {
01070         clc.serverAddress.port = BigShort( PORT_SERVER );
01071     }
01072     Com_Printf( "%s resolved to %i.%i.%i.%i:%i\n", cls.servername,
01073         clc.serverAddress.ip[0], clc.serverAddress.ip[1],
01074         clc.serverAddress.ip[2], clc.serverAddress.ip[3],
01075         BigShort( clc.serverAddress.port ) );
01076 
01077     // if we aren't playing on a lan, we need to authenticate
01078     // with the cd key
01079     if ( NET_IsLocalAddress( clc.serverAddress ) ) {
01080         cls.state = CA_CHALLENGING;
01081     } else {
01082         cls.state = CA_CONNECTING;
01083     }
01084 
01085     cls.keyCatchers = 0;
01086     clc.connectTime = -99999;   // CL_CheckForResend() will fire immediately
01087     clc.connectPacketCount = 0;
01088 
01089     // server connection string
01090     Cvar_Set( "cl_currentServerAddress", server );
01091 }
01092 
01093 
01094 /*
01095 =====================
01096 CL_Rcon_f
01097 
01098   Send the rest of the command line over as
01099   an unconnected command.
01100 =====================
01101 */
01102 void CL_Rcon_f( void ) {
01103     char    message[1024];
01104     netadr_t    to;
01105 
01106     if ( !rcon_client_password->string ) {
01107         Com_Printf ("You must set 'rconpassword' before\n"
01108                     "issuing an rcon command.\n");
01109         return;
01110     }
01111 
01112     message[0] = -1;
01113     message[1] = -1;
01114     message[2] = -1;
01115     message[3] = -1;
01116     message[4] = 0;
01117 
01118     strcat (message, "rcon ");
01119 
01120     strcat (message, rcon_client_password->string);
01121     strcat (message, " ");
01122 
01123     // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=543
01124     strcat (message, Cmd_Cmd()+5);
01125 
01126     if ( cls.state >= CA_CONNECTED ) {
01127         to = clc.netchan.remoteAddress;
01128     } else {
01129         if (!strlen(rconAddress->string)) {
01130             Com_Printf ("You must either be connected,\n"
01131                         "or set the 'rconAddress' cvar\n"
01132                         "to issue rcon commands\n");
01133 
01134             return;
01135         }
01136         NET_StringToAdr (rconAddress->string, &to);
01137         if (to.port == 0) {
01138             to.port = BigShort (PORT_SERVER);
01139         }
01140     }
01141     
01142     NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
01143 }
01144 
01145 /*
01146 =================
01147 CL_SendPureChecksums
01148 =================
01149 */
01150 void CL_SendPureChecksums( void ) {
01151     const char *pChecksums;
01152     char cMsg[MAX_INFO_VALUE];
01153     int i;
01154 
01155     // if we are pure we need to send back a command with our referenced pk3 checksums
01156     pChecksums = FS_ReferencedPakPureChecksums();
01157 
01158     // "cp"
01159     // "Yf"
01160     Com_sprintf(cMsg, sizeof(cMsg), "Yf ");
01161     Q_strcat(cMsg, sizeof(cMsg), va("%d ", cl.serverId) );
01162     Q_strcat(cMsg, sizeof(cMsg), pChecksums);
01163     for (i = 0; i < 2; i++) {
01164         cMsg[i] += 10;
01165     }
01166     CL_AddReliableCommand( cMsg );
01167 }
01168 
01169 /*
01170 =================
01171 CL_ResetPureClientAtServer
01172 =================
01173 */
01174 void CL_ResetPureClientAtServer( void ) {
01175     CL_AddReliableCommand( va("vdr") );
01176 }
01177 
01178 /*
01179 =================
01180 CL_Vid_Restart_f
01181 
01182 Restart the video subsystem
01183 
01184 we also have to reload the UI and CGame because the renderer
01185 doesn't know what graphics to reload
01186 =================
01187 */
01188 void CL_Vid_Restart_f( void ) {
01189 
01190     // don't let them loop during the restart
01191     S_StopAllSounds();
01192     // shutdown the UI
01193     CL_ShutdownUI();
01194     // shutdown the CGame
01195     CL_ShutdownCGame();
01196     // shutdown the renderer and clear the renderer interface
01197     CL_ShutdownRef();
01198     // client is no longer pure untill new checksums are sent
01199     CL_ResetPureClientAtServer();
01200     // clear pak references
01201     FS_ClearPakReferences( FS_UI_REF | FS_CGAME_REF );
01202     // reinitialize the filesystem if the game directory or checksum has changed
01203     FS_ConditionalRestart( clc.checksumFeed );
01204 
01205     cls.rendererStarted = qfalse;
01206     cls.uiStarted = qfalse;
01207     cls.cgameStarted = qfalse;
01208     cls.soundRegistered = qfalse;
01209 
01210     // unpause so the cgame definately gets a snapshot and renders a frame
01211     Cvar_Set( "cl_paused", "0" );
01212 
01213     // if not running a server clear the whole hunk
01214     if ( !com_sv_running->integer ) {
01215         // clear the whole hunk
01216         Hunk_Clear();
01217     }
01218     else {
01219         // clear all the client data on the hunk
01220         Hunk_ClearToMark();
01221     }
01222 
01223     // initialize the renderer interface
01224     CL_InitRef();
01225 
01226     // startup all the client stuff
01227     CL_StartHunkUsers();
01228 
01229     // start the cgame if connected
01230     if ( cls.state > CA_CONNECTED && cls.state != CA_CINEMATIC ) {
01231         cls.cgameStarted = qtrue;
01232         CL_InitCGame();
01233         // send pure checksums
01234         CL_SendPureChecksums();
01235     }
01236 }
01237 
01238 /*
01239 =================
01240 CL_Snd_Restart_f
01241 
01242 Restart the sound subsystem
01243 The cgame and game must also be forced to restart because
01244 handles will be invalid
01245 =================
01246 */
01247 void CL_Snd_Restart_f( void ) {
01248     S_Shutdown();
01249     S_Init();
01250 
01251     CL_Vid_Restart_f();
01252 }
01253 
01254 
01255 /*
01256 ==================
01257 CL_PK3List_f
01258 ==================
01259 */
01260 void CL_OpenedPK3List_f( void ) {
01261     Com_Printf("Opened PK3 Names: %s\n", FS_LoadedPakNames());
01262 }
01263 
01264 /*
01265 ==================
01266 CL_PureList_f
01267 ==================
01268 */
01269 void CL_ReferencedPK3List_f( void ) {
01270     Com_Printf("Referenced PK3 Names: %s\n", FS_ReferencedPakNames());
01271 }
01272 
01273 /*
01274 ==================
01275 CL_Configstrings_f
01276 ==================
01277 */
01278 void CL_Configstrings_f( void ) {
01279     int     i;
01280     int     ofs;
01281 
01282     if ( cls.state != CA_ACTIVE ) {
01283         Com_Printf( "Not connected to a server.\n");
01284         return;
01285     }
01286 
01287     for ( i = 0 ; i < MAX_CONFIGSTRINGS ; i++ ) {
01288         ofs = cl.gameState.stringOffsets[ i ];
01289         if ( !ofs ) {
01290             continue;
01291         }
01292         Com_Printf( "%4i: %s\n", i, cl.gameState.stringData + ofs );
01293     }
01294 }
01295 
01296 /*
01297 ==============
01298 CL_Clientinfo_f
01299 ==============
01300 */
01301 void CL_Clientinfo_f( void ) {
01302     Com_Printf( "--------- Client Information ---------\n" );
01303     Com_Printf( "state: %i\n", cls.state );
01304     Com_Printf( "Server: %s\n", cls.servername );
01305     Com_Printf ("User info settings:\n");
01306     Info_Print( Cvar_InfoString( CVAR_USERINFO ) );
01307     Com_Printf( "--------------------------------------\n" );
01308 }
01309 
01310 
01311 //====================================================================
01312 
01313 /*
01314 =================
01315 CL_DownloadsComplete
01316 
01317 Called when all downloading has been completed
01318 =================
01319 */
01320 void CL_DownloadsComplete( void ) {
01321 
01322     // if we downloaded files we need to restart the file system
01323     if (clc.downloadRestart) {
01324         clc.downloadRestart = qfalse;
01325 
01326         FS_Restart(clc.checksumFeed); // We possibly downloaded a pak, restart the file system to load it
01327 
01328         // inform the server so we get new gamestate info
01329         CL_AddReliableCommand( "donedl" );
01330 
01331         // by sending the donedl command we request a new gamestate
01332         // so we don't want to load stuff yet
01333         return;
01334     }
01335 
01336     // let the client game init and load data
01337     cls.state = CA_LOADING;
01338 
01339     // Pump the loop, this may change gamestate!
01340     Com_EventLoop();
01341 
01342     // if the gamestate was changed by calling Com_EventLoop
01343     // then we loaded everything already and we don't want to do it again.
01344     if ( cls.state != CA_LOADING ) {
01345         return;
01346     }
01347 
01348     // starting to load a map so we get out of full screen ui mode
01349     Cvar_Set("r_uiFullScreen", "0");
01350 
01351     // flush client memory and start loading stuff
01352     // this will also (re)load the UI
01353     // if this is a local client then only the client part of the hunk
01354     // will be cleared, note that this is done after the hunk mark has been set
01355     CL_FlushMemory();
01356 
01357     // initialize the CGame
01358     cls.cgameStarted = qtrue;
01359     CL_InitCGame();
01360 
01361     // set pure checksums
01362     CL_SendPureChecksums();
01363 
01364     CL_WritePacket();
01365     CL_WritePacket();
01366     CL_WritePacket();
01367 }
01368 
01369 /*
01370 =================
01371 CL_BeginDownload
01372 
01373 Requests a file to download from the server.  Stores it in the current
01374 game directory.
01375 =================
01376 */
01377 void CL_BeginDownload( const char *localName, const char *remoteName ) {
01378 
01379     Com_DPrintf("***** CL_BeginDownload *****\n"
01380                 "Localname: %s\n"
01381                 "Remotename: %s\n"
01382                 "****************************\n", localName, remoteName);
01383 
01384     Q_strncpyz ( clc.downloadName, localName, sizeof(clc.downloadName) );
01385     Com_sprintf( clc.downloadTempName, sizeof(clc.downloadTempName), "%s.tmp", localName );
01386 
01387     // Set so UI gets access to it
01388     Cvar_Set( "cl_downloadName", remoteName );
01389     Cvar_Set( "cl_downloadSize", "0" );
01390     Cvar_Set( "cl_downloadCount", "0" );
01391     Cvar_SetValue( "cl_downloadTime", cls.realtime );
01392 
01393     clc.downloadBlock = 0; // Starting new file
01394     clc.downloadCount = 0;
01395 
01396     CL_AddReliableCommand( va("download %s", remoteName) );
01397 }
01398 
01399 /*
01400 =================
01401 CL_NextDownload
01402 
01403 A download completed or failed
01404 =================
01405 */
01406 void CL_NextDownload(void) {
01407     char *s;
01408     char *remoteName, *localName;
01409 
01410     // We are looking to start a download here
01411     if (*clc.downloadList) {
01412         s = clc.downloadList;
01413 
01414         // format is:
01415         //  @remotename@localname@remotename@localname, etc.
01416 
01417         if (*s == '@')
01418             s++;
01419         remoteName = s;
01420         
01421         if ( (s = strchr(s, '@')) == NULL ) {
01422             CL_DownloadsComplete();
01423             return;
01424         }
01425 
01426         *s++ = 0;
01427         localName = s;
01428         if ( (s = strchr(s, '@')) != NULL )
01429             *s++ = 0;
01430         else
01431             s = localName + strlen(localName); // point at the nul byte
01432 
01433         CL_BeginDownload( localName, remoteName );
01434 
01435         clc.downloadRestart = qtrue;
01436 
01437         // move over the rest
01438         memmove( clc.downloadList, s, strlen(s) + 1);
01439 
01440         return;
01441     }
01442 
01443     CL_DownloadsComplete();
01444 }
01445 
01446 /*
01447 =================
01448 CL_InitDownloads
01449 
01450 After receiving a valid game state, we valid the cgame and local zip files here
01451 and determine if we need to download them
01452 =================
01453 */
01454 void CL_InitDownloads(void) {
01455   char missingfiles[1024];
01456 
01457   if ( !cl_allowDownload->integer )
01458   {
01459     // autodownload is disabled on the client
01460     // but it's possible that some referenced files on the server are missing
01461     if (FS_ComparePaks( missingfiles, sizeof( missingfiles ), qfalse ) )
01462     {      
01463       // NOTE TTimo I would rather have that printed as a modal message box
01464       //   but at this point while joining the game we don't know wether we will successfully join or not
01465       Com_Printf( "\nWARNING: You are missing some files referenced by the server:\n%s"
01466                   "You might not be able to join the game\n"
01467                   "Go to the setting menu to turn on autodownload, or get the file elsewhere\n\n", missingfiles );
01468     }
01469   }
01470   else if ( FS_ComparePaks( clc.downloadList, sizeof( clc.downloadList ) , qtrue ) ) {
01471 
01472     Com_Printf("Need paks: %s\n", clc.downloadList );
01473 
01474         if ( *clc.downloadList ) {
01475             // if autodownloading is not enabled on the server
01476             cls.state = CA_CONNECTED;
01477             CL_NextDownload();
01478             return;
01479         }
01480 
01481     }
01482         
01483     CL_DownloadsComplete();
01484 }
01485 
01486 /*
01487 =================
01488 CL_CheckForResend
01489 
01490 Resend a connect message if the last one has timed out
01491 =================
01492 */
01493 void CL_CheckForResend( void ) {
01494     int     port, i;
01495     char    info[MAX_INFO_STRING];
01496     char    data[MAX_INFO_STRING];
01497 
01498     // don't send anything if playing back a demo
01499     if ( clc.demoplaying ) {
01500         return;
01501     }
01502 
01503     // resend if we haven't gotten a reply yet
01504     if ( cls.state != CA_CONNECTING && cls.state != CA_CHALLENGING ) {
01505         return;
01506     }
01507 
01508     if ( cls.realtime - clc.connectTime < RETRANSMIT_TIMEOUT ) {
01509         return;
01510     }
01511 
01512     clc.connectTime = cls.realtime; // for retransmit requests
01513     clc.connectPacketCount++;
01514 
01515 
01516     switch ( cls.state ) {
01517     case CA_CONNECTING:
01518         // requesting a challenge
01519         if ( !Sys_IsLANAddress( clc.serverAddress ) ) {
01520             CL_RequestAuthorization();
01521         }
01522         NET_OutOfBandPrint(NS_CLIENT, clc.serverAddress, "getchallenge");
01523         break;
01524         
01525     case CA_CHALLENGING:
01526         // sending back the challenge
01527         port = Cvar_VariableValue ("net_qport");
01528 
01529         Q_strncpyz( info, Cvar_InfoString( CVAR_USERINFO ), sizeof( info ) );
01530         Info_SetValueForKey( info, "protocol", va("%i", PROTOCOL_VERSION ) );
01531         Info_SetValueForKey( info, "qport", va("%i", port ) );
01532         Info_SetValueForKey( info, "challenge", va("%i", clc.challenge ) );
01533         
01534         strcpy(data, "connect ");
01535     // TTimo adding " " around the userinfo string to avoid truncated userinfo on the server
01536     //   (Com_TokenizeString tokenizes around spaces)
01537     data[8] = '"';
01538 
01539         for(i=0;i<strlen(info);i++) {
01540             data[9+i] = info[i];    // + (clc.challenge)&0x3;
01541         }
01542     data[9+i] = '"';
01543         data[10+i] = 0;
01544 
01545     // NOTE TTimo don't forget to set the right data length!
01546         NET_OutOfBandData( NS_CLIENT, clc.serverAddress, &data[0], i+10 );
01547         // the most current userinfo has been sent, so watch for any
01548         // newer changes to userinfo variables
01549         cvar_modifiedFlags &= ~CVAR_USERINFO;
01550         break;
01551 
01552     default:
01553         Com_Error( ERR_FATAL, "CL_CheckForResend: bad cls.state" );
01554     }
01555 }
01556 
01557 /*
01558 ===================
01559 CL_DisconnectPacket
01560 
01561 Sometimes the server can drop the client and the netchan based
01562 disconnect can be lost.  If the client continues to send packets
01563 to the server, the server will send out of band disconnect packets
01564 to the client so it doesn't have to wait for the full timeout period.
01565 ===================
01566 */
01567 void CL_DisconnectPacket( netadr_t from ) {
01568     if ( cls.state < CA_AUTHORIZING ) {
01569         return;
01570     }
01571 
01572     // if not from our server, ignore it
01573     if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
01574         return;
01575     }
01576 
01577     // if we have received packets within three seconds, ignore it
01578     // (it might be a malicious spoof)
01579     if ( cls.realtime - clc.lastPacketTime < 3000 ) {
01580         return;
01581     }
01582 
01583     // drop the connection
01584     Com_Printf( "Server disconnected for unknown reason\n" );
01585     Cvar_Set("com_errorMessage", "Server disconnected for unknown reason\n" );
01586     CL_Disconnect( qtrue );
01587 }
01588 
01589 
01590 /*
01591 ===================
01592 CL_MotdPacket
01593 
01594 ===================
01595 */
01596 void CL_MotdPacket( netadr_t from ) {
01597     char    *challenge;
01598     char    *info;
01599 
01600     // if not from our server, ignore it
01601     if ( !NET_CompareAdr( from, cls.updateServer ) ) {
01602         return;
01603     }
01604 
01605     info = Cmd_Argv(1);
01606 
01607     // check challenge
01608     challenge = Info_ValueForKey( info, "challenge" );
01609     if ( strcmp( challenge, cls.updateChallenge ) ) {
01610         return;
01611     }
01612 
01613     challenge = Info_ValueForKey( info, "motd" );
01614 
01615     Q_strncpyz( cls.updateInfoString, info, sizeof( cls.updateInfoString ) );
01616     Cvar_Set( "cl_motdString", challenge );
01617 }
01618 
01619 /*
01620 ===================
01621 CL_InitServerInfo
01622 ===================
01623 */
01624 void CL_InitServerInfo( serverInfo_t *server, serverAddress_t *address ) {
01625     server->adr.type  = NA_IP;
01626     server->adr.ip[0] = address->ip[0];
01627     server->adr.ip[1] = address->ip[1];
01628     server->adr.ip[2] = address->ip[2];
01629     server->adr.ip[3] = address->ip[3];
01630     server->adr.port  = address->port;
01631     server->clients = 0;
01632     server->hostName[0] = '\0';
01633     server->mapName[0] = '\0';
01634     server->maxClients = 0;
01635     server->maxPing = 0;
01636     server->minPing = 0;
01637     server->ping = -1;
01638     server->game[0] = '\0';
01639     server->gameType = 0;
01640     server->netType = 0;
01641 }
01642 
01643 #define MAX_SERVERSPERPACKET    256
01644 
01645 /*
01646 ===================
01647 CL_ServersResponsePacket
01648 ===================
01649 */
01650 void CL_ServersResponsePacket( netadr_t from, msg_t *msg ) {
01651     int             i, count, max, total;
01652     serverAddress_t addresses[MAX_SERVERSPERPACKET];
01653     int             numservers;
01654     byte*           buffptr;
01655     byte*           buffend;
01656     
01657     Com_Printf("CL_ServersResponsePacket\n");
01658 
01659     if (cls.numglobalservers == -1) {
01660         // state to detect lack of servers or lack of response
01661         cls.numglobalservers = 0;
01662         cls.numGlobalServerAddresses = 0;
01663     }
01664 
01665     if (cls.nummplayerservers == -1) {
01666         cls.nummplayerservers = 0;
01667     }
01668 
01669     // parse through server response string
01670     numservers = 0;
01671     buffptr    = msg->data;
01672     buffend    = buffptr + msg->cursize;
01673     while (buffptr+1 < buffend) {
01674         // advance to initial token
01675         do {
01676             if (*buffptr++ == '\\')
01677                 break;      
01678         }
01679         while (buffptr < buffend);
01680 
01681         if ( buffptr >= buffend - 6 ) {
01682             break;
01683         }
01684 
01685         // parse out ip
01686         addresses[numservers].ip[0] = *buffptr++;
01687         addresses[numservers].ip[1] = *buffptr++;
01688         addresses[numservers].ip[2] = *buffptr++;
01689         addresses[numservers].ip[3] = *buffptr++;
01690 
01691         // parse out port
01692         addresses[numservers].port = (*buffptr++)<<8;
01693         addresses[numservers].port += *buffptr++;
01694         addresses[numservers].port = BigShort( addresses[numservers].port );
01695 
01696         // syntax check
01697         if (*buffptr != '\\') {
01698             break;
01699         }
01700 
01701         Com_DPrintf( "server: %d ip: %d.%d.%d.%d:%d\n",numservers,
01702                 addresses[numservers].ip[0],
01703                 addresses[numservers].ip[1],
01704                 addresses[numservers].ip[2],
01705                 addresses[numservers].ip[3],
01706                 addresses[numservers].port );
01707 
01708         numservers++;
01709         if (numservers >= MAX_SERVERSPERPACKET) {
01710             break;
01711         }
01712 
01713         // parse out EOT
01714         if (buffptr[1] == 'E' && buffptr[2] == 'O' && buffptr[3] == 'T') {
01715             break;
01716         }
01717     }
01718 
01719     if (cls.masterNum == 0) {
01720         count = cls.numglobalservers;
01721         max = MAX_GLOBAL_SERVERS;
01722     } else {
01723         count = cls.nummplayerservers;
01724         max = MAX_OTHER_SERVERS;
01725     }
01726 
01727     for (i = 0; i < numservers && count < max; i++) {
01728         // build net address
01729         serverInfo_t *server = (cls.masterNum == 0) ? &cls.globalServers[count] : &cls.mplayerServers[count];
01730 
01731         CL_InitServerInfo( server, &addresses[i] );
01732         // advance to next slot
01733         count++;
01734     }
01735 
01736     // if getting the global list
01737     if (cls.masterNum == 0) {
01738         if ( cls.numGlobalServerAddresses < MAX_GLOBAL_SERVERS ) {
01739             // if we couldn't store the servers in the main list anymore
01740             for (; i < numservers && count >= max; i++) {
01741                 serverAddress_t *addr;
01742                 // just store the addresses in an additional list
01743                 addr = &cls.globalServerAddresses[cls.numGlobalServerAddresses++];
01744                 addr->ip[0] = addresses[i].ip[0];
01745                 addr->ip[1] = addresses[i].ip[1];
01746                 addr->ip[2] = addresses[i].ip[2];
01747                 addr->ip[3] = addresses[i].ip[3];
01748                 addr->port  = addresses[i].port;
01749             }
01750         }
01751     }
01752 
01753     if (cls.masterNum == 0) {
01754         cls.numglobalservers = count;
01755         total = count + cls.numGlobalServerAddresses;
01756     } else {
01757         cls.nummplayerservers = count;
01758         total = count;
01759     }
01760 
01761     Com_Printf("%d servers parsed (total %d)\n", numservers, total);
01762 }
01763 
01764 /*
01765 =================
01766 CL_ConnectionlessPacket
01767 
01768 Responses to broadcasts, etc
01769 =================
01770 */
01771 void CL_ConnectionlessPacket( netadr_t from, msg_t *msg ) {
01772     char    *s;
01773     char    *c;
01774 
01775     MSG_BeginReadingOOB( msg );
01776     MSG_ReadLong( msg );    // skip the -1
01777 
01778     s = MSG_ReadStringLine( msg );
01779 
01780     Cmd_TokenizeString( s );
01781 
01782     c = Cmd_Argv(0);
01783 
01784     Com_DPrintf ("CL packet %s: %s\n", NET_AdrToString(from), c);
01785 
01786     // challenge from the server we are connecting to
01787     if ( !Q_stricmp(c, "challengeResponse") ) {
01788         if ( cls.state != CA_CONNECTING ) {
01789             Com_Printf( "Unwanted challenge response received.  Ignored.\n" );
01790         } else {
01791             // start sending challenge repsonse instead of challenge request packets
01792             clc.challenge = atoi(Cmd_Argv(1));
01793             cls.state = CA_CHALLENGING;
01794             clc.connectPacketCount = 0;
01795             clc.connectTime = -99999;
01796 
01797             // take this address as the new server address.  This allows
01798             // a server proxy to hand off connections to multiple servers
01799             clc.serverAddress = from;
01800             Com_DPrintf ("challengeResponse: %d\n", clc.challenge);
01801         }
01802         return;
01803     }
01804 
01805     // server connection
01806     if ( !Q_stricmp(c, "connectResponse") ) {
01807         if ( cls.state >= CA_CONNECTED ) {
01808             Com_Printf ("Dup connect received.  Ignored.\n");
01809             return;
01810         }
01811         if ( cls.state != CA_CHALLENGING ) {
01812             Com_Printf ("connectResponse packet while not connecting.  Ignored.\n");
01813             return;
01814         }
01815         if ( !NET_CompareBaseAdr( from, clc.serverAddress ) ) {
01816             Com_Printf( "connectResponse from a different address.  Ignored.\n" );
01817             Com_Printf( "%s should have been %s\n", NET_AdrToString( from ), 
01818                 NET_AdrToString( clc.serverAddress ) );
01819             return;
01820         }
01821         Netchan_Setup (NS_CLIENT, &clc.netchan, from, Cvar_VariableValue( "net_qport" ) );
01822         cls.state = CA_CONNECTED;
01823         clc.lastPacketSentTime = -9999;     // send first packet immediately
01824         return;
01825     }
01826 
01827     // server responding to an info broadcast
01828     if ( !Q_stricmp(c, "infoResponse") ) {
01829         CL_ServerInfoPacket( from, msg );
01830         return;
01831     }
01832 
01833     // server responding to a get playerlist
01834     if ( !Q_stricmp(c, "statusResponse") ) {
01835         CL_ServerStatusResponse( from, msg );
01836         return;
01837     }
01838 
01839     // a disconnect message from the server, which will happen if the server
01840     // dropped the connection but it is still getting packets from us
01841     if (!Q_stricmp(c, "disconnect")) {
01842         CL_DisconnectPacket( from );
01843         return;
01844     }
01845 
01846     // echo request from server
01847     if ( !Q_stricmp(c, "echo") ) {
01848         NET_OutOfBandPrint( NS_CLIENT, from, "%s", Cmd_Argv(1) );
01849         return;
01850     }
01851 
01852     // cd check
01853     if ( !Q_stricmp(c, "keyAuthorize") ) {
01854         // we don't use these now, so dump them on the floor
01855         return;
01856     }
01857 
01858     // global MOTD from id
01859     if ( !Q_stricmp(c, "motd") ) {
01860         CL_MotdPacket( from );
01861         return;
01862     }
01863 
01864     // echo request from server
01865     if ( !Q_stricmp(c, "print") ) {
01866         s = MSG_ReadString( msg );
01867         Q_strncpyz( clc.serverMessage, s, sizeof( clc.serverMessage ) );
01868         Com_Printf( "%s", s );
01869         return;
01870     }
01871 
01872     // echo request from server
01873     if ( !Q_strncmp(c, "getserversResponse", 18) ) {
01874         CL_ServersResponsePacket( from, msg );
01875         return;
01876     }
01877 
01878     Com_DPrintf ("Unknown connectionless packet command.\n");
01879 }
01880 
01881 
01882 /*
01883 =================
01884 CL_PacketEvent
01885 
01886 A packet has arrived from the main event loop
01887 =================
01888 */
01889 void CL_PacketEvent( netadr_t from, msg_t *msg ) {
01890     int     headerBytes;
01891 
01892     clc.lastPacketTime = cls.realtime;
01893 
01894     if ( msg->cursize >= 4 && *(int *)msg->data == -1 ) {
01895         CL_ConnectionlessPacket( from, msg );
01896         return;
01897     }
01898 
01899     if ( cls.state < CA_CONNECTED ) {
01900         return;     // can't be a valid sequenced packet
01901     }
01902 
01903     if ( msg->cursize < 4 ) {
01904         Com_Printf ("%s: Runt packet\n",NET_AdrToString( from ));
01905         return;
01906     }
01907 
01908     //
01909     // packet from server
01910     //
01911     if ( !NET_CompareAdr( from, clc.netchan.remoteAddress ) ) {
01912         Com_DPrintf ("%s:sequenced packet without connection\n"
01913             ,NET_AdrToString( from ) );
01914         // FIXME: send a client disconnect?
01915         return;
01916     }
01917 
01918     if (!CL_Netchan_Process( &clc.netchan, msg) ) {
01919         return;     // out of order, duplicated, etc
01920     }
01921 
01922     // the header is different lengths for reliable and unreliable messages
01923     headerBytes = msg->readcount;
01924 
01925     // track the last message received so it can be returned in 
01926     // client messages, allowing the server to detect a dropped
01927     // gamestate
01928     clc.serverMessageSequence = LittleLong( *(int *)msg->data );
01929 
01930     clc.lastPacketTime = cls.realtime;
01931     CL_ParseServerMessage( msg );
01932 
01933     //
01934     // we don't know if it is ok to save a demo message until
01935     // after we have parsed the frame
01936     //
01937     if ( clc.demorecording && !clc.demowaiting ) {
01938         CL_WriteDemoMessage( msg, headerBytes );
01939     }
01940 }
01941 
01942 /*
01943 ==================
01944 CL_CheckTimeout
01945 
01946 ==================
01947 */
01948 void CL_CheckTimeout( void ) {
01949     //
01950     // check timeout
01951     //
01952     if ( ( !cl_paused->integer || !sv_paused->integer ) 
01953         && cls.state >= CA_CONNECTED && cls.state != CA_CINEMATIC
01954         && cls.realtime - clc.lastPacketTime > cl_timeout->value*1000) {
01955         if (++cl.timeoutcount > 5) {    // timeoutcount saves debugger
01956             Com_Printf ("\nServer connection timed out.\n");
01957             CL_Disconnect( qtrue );
01958             return;
01959         }
01960     } else {
01961         cl.timeoutcount = 0;
01962     }
01963 }
01964 
01965 
01966 //============================================================================
01967 
01968 /*
01969 ==================
01970 CL_CheckUserinfo
01971 
01972 ==================
01973 */
01974 void CL_CheckUserinfo( void ) {
01975     // don't add reliable commands when not yet connected
01976     if ( cls.state < CA_CHALLENGING ) {
01977         return;
01978     }
01979     // don't overflow the reliable command buffer when paused
01980     if ( cl_paused->integer ) {
01981         return;
01982     }
01983     // send a reliable userinfo update if needed
01984     if ( cvar_modifiedFlags & CVAR_USERINFO ) {
01985         cvar_modifiedFlags &= ~CVAR_USERINFO;
01986         CL_AddReliableCommand( va("userinfo \"%s\"", Cvar_InfoString( CVAR_USERINFO ) ) );
01987     }
01988 
01989 }
01990 
01991 /*
01992 ==================
01993 CL_Frame
01994 
01995 ==================
01996 */
01997 void CL_Frame ( int msec ) {
01998 
01999     if ( !com_cl_running->integer ) {
02000         return;
02001     }
02002 
02003     if ( cls.cddialog ) {
02004         // bring up the cd error dialog if needed
02005         cls.cddialog = qfalse;
02006         VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_NEED_CD );
02007     } else  if ( cls.state == CA_DISCONNECTED && !( cls.keyCatchers & KEYCATCH_UI )
02008         && !com_sv_running->integer ) {
02009         // if disconnected, bring up the menu
02010         S_StopAllSounds();
02011         VM_Call( uivm, UI_SET_ACTIVE_MENU, UIMENU_MAIN );
02012     }
02013 
02014     // if recording an avi, lock to a fixed fps
02015     if ( cl_avidemo->integer && msec) {
02016         // save the current screen
02017         if ( cls.state == CA_ACTIVE || cl_forceavidemo->integer) {
02018             Cbuf_ExecuteText( EXEC_NOW, "screenshot silent\n" );
02019         }
02020         // fixed time for next frame'
02021         msec = (1000 / cl_avidemo->integer) * com_timescale->value;
02022         if (msec == 0) {
02023             msec = 1;
02024         }
02025     }
02026     
02027     // save the msec before checking pause
02028     cls.realFrametime = msec;
02029 
02030     // decide the simulation time
02031     cls.frametime = msec;
02032 
02033     cls.realtime += cls.frametime;
02034 
02035     if ( cl_timegraph->integer ) {
02036         SCR_DebugGraph ( cls.realFrametime * 0.25, 0 );
02037     }
02038 
02039     // see if we need to update any userinfo
02040     CL_CheckUserinfo();
02041 
02042     // if we haven't gotten a packet in a long time,
02043     // drop the connection
02044     CL_CheckTimeout();
02045 
02046     // send intentions now
02047     CL_SendCmd();
02048 
02049     // resend a connection request if necessary
02050     CL_CheckForResend();
02051 
02052     // decide on the serverTime to render
02053     CL_SetCGameTime();
02054 
02055     // update the screen
02056     SCR_UpdateScreen();
02057 
02058     // update audio
02059     S_Update();
02060 
02061     // advance local effects for next frame
02062     SCR_RunCinematic();
02063 
02064     Con_RunConsole();
02065 
02066     cls.framecount++;
02067 }
02068 
02069 
02070 //============================================================================
02071 
02072 /*
02073 ================
02074 CL_RefPrintf
02075 
02076 DLL glue
02077 ================
02078 */
02079 void QDECL CL_RefPrintf( int print_level, const char *fmt, ...) {
02080     va_list     argptr;
02081     char        msg[MAXPRINTMSG];
02082     
02083     va_start (argptr,fmt);
02084     Q_vsnprintf (msg, sizeof(msg), fmt, argptr);
02085     va_end (argptr);
02086 
02087     if ( print_level == PRINT_ALL ) {
02088         Com_Printf ("%s", msg);
02089     } else if ( print_level == PRINT_WARNING ) {
02090         Com_Printf (S_COLOR_YELLOW "%s", msg);      // yellow
02091     } else if ( print_level == PRINT_DEVELOPER ) {
02092         Com_DPrintf (S_COLOR_RED "%s", msg);        // red
02093     }
02094 }
02095 
02096 
02097 
02098 /*
02099 ============
02100 CL_ShutdownRef
02101 ============
02102 */
02103 void CL_ShutdownRef( void ) {
02104     if ( !re.Shutdown ) {
02105         return;
02106     }
02107     re.Shutdown( qtrue );
02108     Com_Memset( &re, 0, sizeof( re ) );
02109 }
02110 
02111 /*
02112 ============
02113 CL_InitRenderer
02114 ============
02115 */
02116 void CL_InitRenderer( void ) {
02117     // this sets up the renderer and calls R_Init
02118     re.BeginRegistration( &cls.glconfig );
02119 
02120     // load character sets
02121     cls.charSetShader = re.RegisterShader( "gfx/2d/bigchars" );
02122     cls.whiteShader = re.RegisterShader( "white" );
02123     cls.consoleShader = re.RegisterShader( "console" );
02124     g_console_field_width = cls.glconfig.vidWidth / SMALLCHAR_WIDTH - 2;
02125     g_consoleField.widthInChars = g_console_field_width;
02126 }
02127 
02128 /*
02129 ============================
02130 CL_StartHunkUsers
02131 
02132 After the server has cleared the hunk, these will need to be restarted
02133 This is the only place that any of these functions are called from
02134 ============================
02135 */
02136 void CL_StartHunkUsers( void ) {
02137     if (!com_cl_running) {
02138         return;
02139     }
02140 
02141     if ( !com_cl_running->integer ) {
02142         return;
02143     }
02144 
02145     if ( !cls.rendererStarted ) {
02146         cls.rendererStarted = qtrue;
02147         CL_InitRenderer();
02148     }
02149 
02150     if ( !cls.soundStarted ) {
02151         cls.soundStarted = qtrue;
02152         S_Init();
02153     }
02154 
02155     if ( !cls.soundRegistered ) {
02156         cls.soundRegistered = qtrue;
02157         S_BeginRegistration();
02158     }
02159 
02160     if ( !cls.uiStarted ) {
02161         cls.uiStarted = qtrue;
02162         CL_InitUI();
02163     }
02164 }
02165 
02166 /*
02167 ============
02168 CL_RefMalloc
02169 ============
02170 */
02171 void *CL_RefMalloc( int size ) {
02172     return Z_TagMalloc( size, TAG_RENDERER );
02173 }
02174 
02175 int CL_ScaledMilliseconds(void) {
02176     return Sys_Milliseconds()*com_timescale->value;
02177 }
02178 
02179 /*
02180 ============
02181 CL_InitRef
02182 ============
02183 */
02184 void CL_InitRef( void ) {
02185     refimport_t ri;
02186     refexport_t *ret;
02187 
02188     Com_Printf( "----- Initializing Renderer ----\n" );
02189 
02190     ri.Cmd_AddCommand = Cmd_AddCommand;
02191     ri.Cmd_RemoveCommand = Cmd_RemoveCommand;
02192     ri.Cmd_Argc = Cmd_Argc;
02193     ri.Cmd_Argv = Cmd_Argv;
02194     ri.Cmd_ExecuteText = Cbuf_ExecuteText;
02195     ri.Printf = CL_RefPrintf;
02196     ri.Error = Com_Error;
02197     ri.Milliseconds = CL_ScaledMilliseconds;
02198     ri.Malloc = CL_RefMalloc;
02199     ri.Free = Z_Free;
02200 #ifdef HUNK_DEBUG
02201     ri.Hunk_AllocDebug = Hunk_AllocDebug;
02202 #else
02203     ri.Hunk_Alloc = Hunk_Alloc;
02204 #endif
02205     ri.Hunk_AllocateTempMemory = Hunk_AllocateTempMemory;
02206     ri.Hunk_FreeTempMemory = Hunk_FreeTempMemory;
02207     ri.CM_DrawDebugSurface = CM_DrawDebugSurface;
02208     ri.FS_ReadFile = FS_ReadFile;
02209     ri.FS_FreeFile = FS_FreeFile;
02210     ri.FS_WriteFile = FS_WriteFile;
02211     ri.FS_FreeFileList = FS_FreeFileList;
02212     ri.FS_ListFiles = FS_ListFiles;
02213     ri.FS_FileIsInPAK = FS_FileIsInPAK;
02214     ri.FS_FileExists = FS_FileExists;
02215     ri.Cvar_Get = Cvar_Get;
02216     ri.Cvar_Set = Cvar_Set;
02217 
02218     // cinematic stuff
02219 
02220     ri.CIN_UploadCinematic = CIN_UploadCinematic;
02221     ri.CIN_PlayCinematic = CIN_PlayCinematic;
02222     ri.CIN_RunCinematic = CIN_RunCinematic;
02223 
02224     ret = GetRefAPI( REF_API_VERSION, &ri );
02225 
02226 #if defined __USEA3D && defined __A3D_GEOM
02227     hA3Dg_ExportRenderGeom (ret);
02228 #endif
02229 
02230     Com_Printf( "-------------------------------\n");
02231 
02232     if ( !ret ) {
02233         Com_Error (ERR_FATAL, "Couldn't initialize refresh" );
02234     }
02235 
02236     re = *ret;
02237 
02238     // unpause so the cgame definately gets a snapshot and renders a frame
02239     Cvar_Set( "cl_paused", "0" );
02240 }
02241 
02242 
02243 //===========================================================================================
02244 
02245 
02246 void CL_SetModel_f( void ) {
02247     char    *arg;
02248     char    name[256];
02249 
02250     arg = Cmd_Argv( 1 );
02251     if (arg[0]) {
02252         Cvar_Set( "model", arg );
02253         Cvar_Set( "headmodel", arg );
02254     } else {
02255         Cvar_VariableStringBuffer( "model", name, sizeof(name) );
02256         Com_Printf("model is set to %s\n", name);
02257     }
02258 }
02259 
02260 /*
02261 ====================
02262 CL_Init
02263 ====================
02264 */
02265 void CL_Init( void ) {
02266     Com_Printf( "----- Client Initialization -----\n" );
02267 
02268     Con_Init ();    
02269 
02270     CL_ClearState ();
02271 
02272     cls.state = CA_DISCONNECTED;    // no longer CA_UNINITIALIZED
02273 
02274     cls.realtime = 0;
02275 
02276     CL_InitInput ();
02277 
02278     //
02279     // register our variables
02280     //
02281     cl_noprint = Cvar_Get( "cl_noprint", "0", 0 );
02282     cl_motd = Cvar_Get ("cl_motd", "1", 0);
02283 
02284     cl_timeout = Cvar_Get ("cl_timeout", "200", 0);
02285 
02286     cl_timeNudge = Cvar_Get ("cl_timeNudge", "0", CVAR_TEMP );
02287     cl_shownet = Cvar_Get ("cl_shownet", "0", CVAR_TEMP );
02288     cl_showSend = Cvar_Get ("cl_showSend", "0", CVAR_TEMP );
02289     cl_showTimeDelta = Cvar_Get ("cl_showTimeDelta", "0", CVAR_TEMP );
02290     cl_freezeDemo = Cvar_Get ("cl_freezeDemo", "0", CVAR_TEMP );
02291     rcon_client_password = Cvar_Get ("rconPassword", "", CVAR_TEMP );
02292     cl_activeAction = Cvar_Get( "activeAction", "", CVAR_TEMP );
02293 
02294     cl_timedemo = Cvar_Get ("timedemo", "0", 0);
02295     cl_avidemo = Cvar_Get ("cl_avidemo", "0", 0);
02296     cl_forceavidemo = Cvar_Get ("cl_forceavidemo", "0", 0);
02297 
02298     rconAddress = Cvar_Get ("rconAddress", "", 0);
02299 
02300     cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", CVAR_ARCHIVE);
02301     cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "140", CVAR_ARCHIVE);
02302     cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
02303 
02304     cl_maxpackets = Cvar_Get ("cl_maxpackets", "30", CVAR_ARCHIVE );
02305     cl_packetdup = Cvar_Get ("cl_packetdup", "1", CVAR_ARCHIVE );
02306 
02307     cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE);
02308     cl_sensitivity = Cvar_Get ("sensitivity", "5", CVAR_ARCHIVE);
02309     cl_mouseAccel = Cvar_Get ("cl_mouseAccel", "0", CVAR_ARCHIVE);
02310     cl_freelook = Cvar_Get( "cl_freelook", "1", CVAR_ARCHIVE );
02311 
02312     cl_showMouseRate = Cvar_Get ("cl_showmouserate", "0", 0);
02313 
02314     cl_allowDownload = Cvar_Get ("cl_allowDownload", "0", CVAR_ARCHIVE);
02315 
02316     cl_conXOffset = Cvar_Get ("cl_conXOffset", "0", 0);
02317 #ifdef MACOS_X
02318         // In game video is REALLY slow in Mac OS X right now due to driver slowness
02319     cl_inGameVideo = Cvar_Get ("r_inGameVideo", "0", CVAR_ARCHIVE);
02320 #else
02321     cl_inGameVideo = Cvar_Get ("r_inGameVideo", "1", CVAR_ARCHIVE);
02322 #endif
02323 
02324     cl_serverStatusResendTime = Cvar_Get ("cl_serverStatusResendTime", "750", 0);
02325 
02326     // init autoswitch so the ui will have it correctly even
02327     // if the cgame hasn't been started
02328     Cvar_Get ("cg_autoswitch", "1", CVAR_ARCHIVE);
02329 
02330     m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
02331     m_yaw = Cvar_Get ("m_yaw", "0.022", CVAR_ARCHIVE);
02332     m_forward = Cvar_Get ("m_forward", "0.25", CVAR_ARCHIVE);
02333     m_side = Cvar_Get ("m_side", "0.25", CVAR_ARCHIVE);
02334 #ifdef MACOS_X
02335         // Input is jittery on OS X w/o this
02336     m_filter = Cvar_Get ("m_filter", "1", CVAR_ARCHIVE);
02337 #else
02338     m_filter = Cvar_Get ("m_filter", "0", CVAR_ARCHIVE);
02339 #endif
02340 
02341     cl_motdString = Cvar_Get( "cl_motdString", "", CVAR_ROM );
02342 
02343     Cvar_Get( "cl_maxPing", "800", CVAR_ARCHIVE );
02344 
02345 
02346     // userinfo
02347     Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE );
02348     Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE );
02349     Cvar_Get ("snaps", "20", CVAR_USERINFO | CVAR_ARCHIVE );
02350     Cvar_Get ("model", "sarge", CVAR_USERINFO | CVAR_ARCHIVE );
02351     Cvar_Get ("headmodel", "sarge", CVAR_USERINFO | CVAR_ARCHIVE );
02352     Cvar_Get ("team_model", "james", CVAR_USERINFO | CVAR_ARCHIVE );
02353     Cvar_Get ("team_headmodel", "*james", CVAR_USERINFO | CVAR_ARCHIVE );
02354     Cvar_Get ("g_redTeam", "Stroggs", CVAR_SERVERINFO | CVAR_ARCHIVE);
02355     Cvar_Get ("g_blueTeam", "Pagans", CVAR_SERVERINFO | CVAR_ARCHIVE);
02356     Cvar_Get ("color1",  "4", CVAR_USERINFO | CVAR_ARCHIVE );
02357     Cvar_Get ("color2", "5", CVAR_USERINFO | CVAR_ARCHIVE );
02358     Cvar_Get ("handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE );
02359     Cvar_Get ("teamtask", "0", CVAR_USERINFO );
02360     Cvar_Get ("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE );
02361     Cvar_Get ("cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE );
02362 
02363     Cvar_Get ("password", "", CVAR_USERINFO);
02364     Cvar_Get ("cg_predictItems", "1", CVAR_USERINFO | CVAR_ARCHIVE );
02365 
02366 
02367     // cgame might not be initialized before menu is used
02368     Cvar_Get ("cg_viewsize", "100", CVAR_ARCHIVE );
02369 
02370     //
02371     // register our commands
02372     //
02373     Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
02374     Cmd_AddCommand ("configstrings", CL_Configstrings_f);
02375     Cmd_AddCommand ("clientinfo", CL_Clientinfo_f);
02376     Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
02377     Cmd_AddCommand ("vid_restart", CL_Vid_Restart_f);
02378     Cmd_AddCommand ("disconnect", CL_Disconnect_f);
02379     Cmd_AddCommand ("record", CL_Record_f);
02380     Cmd_AddCommand ("demo", CL_PlayDemo_f);
02381     Cmd_AddCommand ("cinematic", CL_PlayCinematic_f);
02382     Cmd_AddCommand ("stoprecord", CL_StopRecord_f);
02383     Cmd_AddCommand ("connect", CL_Connect_f);
02384     Cmd_AddCommand ("reconnect", CL_Reconnect_f);
02385     Cmd_AddCommand ("localservers", CL_LocalServers_f);
02386     Cmd_AddCommand ("globalservers", CL_GlobalServers_f);
02387     Cmd_AddCommand ("rcon", CL_Rcon_f);
02388     Cmd_AddCommand ("setenv", CL_Setenv_f );
02389     Cmd_AddCommand ("ping", CL_Ping_f );
02390     Cmd_AddCommand ("serverstatus", CL_ServerStatus_f );
02391     Cmd_AddCommand ("showip", CL_ShowIP_f );
02392     Cmd_AddCommand ("fs_openedList", CL_OpenedPK3List_f );
02393     Cmd_AddCommand ("fs_referencedList", CL_ReferencedPK3List_f );
02394     Cmd_AddCommand ("model", CL_SetModel_f );
02395     CL_InitRef();
02396 
02397     SCR_Init ();
02398 
02399     Cbuf_Execute ();
02400 
02401     Cvar_Set( "cl_running", "1" );
02402 
02403     Com_Printf( "----- Client Initialization Complete -----\n" );
02404 }
02405 
02406 
02407 /*
02408 ===============
02409 CL_Shutdown
02410 
02411 ===============
02412 */
02413 void CL_Shutdown( void ) {
02414     static qboolean recursive = qfalse;
02415     
02416     Com_Printf( "----- CL_Shutdown -----\n" );
02417 
02418     if ( recursive ) {
02419         printf ("recursive shutdown\n");
02420         return;
02421     }
02422     recursive = qtrue;
02423 
02424     CL_Disconnect( qtrue );
02425 
02426     S_Shutdown();
02427     CL_ShutdownRef();
02428     
02429     CL_ShutdownUI();
02430 
02431     Cmd_RemoveCommand ("cmd");
02432     Cmd_RemoveCommand ("configstrings");
02433     Cmd_RemoveCommand ("userinfo");
02434     Cmd_RemoveCommand ("snd_restart");
02435     Cmd_RemoveCommand ("vid_restart");
02436     Cmd_RemoveCommand ("disconnect");
02437     Cmd_RemoveCommand ("record");
02438     Cmd_RemoveCommand ("demo");
02439     Cmd_RemoveCommand ("cinematic");
02440     Cmd_RemoveCommand ("stoprecord");
02441     Cmd_RemoveCommand ("connect");
02442     Cmd_RemoveCommand ("localservers");
02443     Cmd_RemoveCommand ("globalservers");
02444     Cmd_RemoveCommand ("rcon");
02445     Cmd_RemoveCommand ("setenv");
02446     Cmd_RemoveCommand ("ping");
02447     Cmd_RemoveCommand ("serverstatus");
02448     Cmd_RemoveCommand ("showip");
02449     Cmd_RemoveCommand ("model");
02450 
02451     Cvar_Set( "cl_running", "0" );
02452 
02453     recursive = qfalse;
02454 
02455     Com_Memset( &cls, 0, sizeof( cls ) );
02456 
02457     Com_Printf( "-----------------------\n" );
02458 
02459 }
02460 
02461 static void CL_SetServerInfo(serverInfo_t *server, const char *info, int ping) {
02462     if (server) {
02463         if (info) {
02464             server->clients = atoi(Info_ValueForKey(info, "clients"));
02465             Q_strncpyz(server->hostName,Info_ValueForKey(info, "hostname"), MAX_NAME_LENGTH);
02466             Q_strncpyz(server->mapName, Info_ValueForKey(info, "mapname"), MAX_NAME_LENGTH);
02467             server->maxClients = atoi(Info_ValueForKey(info, "sv_maxclients"));
02468             Q_strncpyz(server->game,Info_ValueForKey(info, "game"), MAX_NAME_LENGTH);
02469             server->gameType = atoi(Info_ValueForKey(info, "gametype"));
02470             server->netType = atoi(Info_ValueForKey(info, "nettype"));
02471             server->minPing = atoi(Info_ValueForKey(info, "minping"));
02472             server->maxPing = atoi(Info_ValueForKey(info, "maxping"));
02473             server->punkbuster = atoi(Info_ValueForKey(info, "punkbuster"));
02474         }
02475         server->ping = ping;
02476     }
02477 }
02478 
02479 static void CL_SetServerInfoByAddress(netadr_t from, const char *info, int ping) {
02480     int i;
02481 
02482     for (i = 0; i < MAX_OTHER_SERVERS; i++) {
02483         if (NET_CompareAdr(from, cls.localServers[i].adr)) {
02484             CL_SetServerInfo(&cls.localServers[i], info, ping);
02485         }
02486     }
02487 
02488     for (i = 0; i < MAX_OTHER_SERVERS; i++) {
02489         if (NET_CompareAdr(from, cls.mplayerServers[i].adr)) {
02490             CL_SetServerInfo(&cls.mplayerServers[i], info, ping);
02491         }
02492     }
02493 
02494     for (i = 0; i < MAX_GLOBAL_SERVERS; i++) {
02495         if (NET_CompareAdr(from, cls.globalServers[i].adr)) {
02496             CL_SetServerInfo(&cls.globalServers[i], info, ping);
02497         }
02498     }
02499 
02500     for (i = 0; i < MAX_OTHER_SERVERS; i++) {
02501         if (NET_CompareAdr(from, cls.favoriteServers[i].adr)) {
02502             CL_SetServerInfo(&cls.favoriteServers[i], info, ping);
02503         }
02504     }
02505 
02506 }
02507 
02508 /*
02509 ===================
02510 CL_ServerInfoPacket
02511 ===================
02512 */
02513 void CL_ServerInfoPacket( netadr_t from, msg_t *msg ) {
02514     int     i, type;
02515     char    info[MAX_INFO_STRING];
02516     char*   str;
02517     char    *infoString;
02518     int     prot;
02519 
02520     infoString = MSG_ReadString( msg );
02521 
02522     // if this isn't the correct protocol version, ignore it
02523     prot = atoi( Info_ValueForKey( infoString, "protocol" ) );
02524     if ( prot != PROTOCOL_VERSION ) {
02525         Com_DPrintf( "Different protocol info packet: %s\n", infoString );
02526         return;
02527     }
02528 
02529     // iterate servers waiting for ping response
02530     for (i=0; i<MAX_PINGREQUESTS; i++)
02531     {
02532         if ( cl_pinglist[i].adr.port && !cl_pinglist[i].time && NET_CompareAdr( from, cl_pinglist[i].adr ) )
02533         {
02534             // calc ping time
02535             cl_pinglist[i].time = cls.realtime - cl_pinglist[i].start + 1;
02536             Com_DPrintf( "ping time %dms from %s\n", cl_pinglist[i].time, NET_AdrToString( from ) );
02537 
02538             // save of info
02539             Q_strncpyz( cl_pinglist[i].info, infoString, sizeof( cl_pinglist[i].info ) );
02540 
02541             // tack on the net type
02542             // NOTE: make sure these types are in sync with the netnames strings in the UI
02543             switch (from.type)
02544             {
02545                 case NA_BROADCAST:
02546                 case NA_IP:
02547                     str = "udp";
02548                     type = 1;
02549                     break;
02550 
02551                 case NA_IPX:
02552                 case NA_BROADCAST_IPX:
02553                     str = "ipx";
02554                     type = 2;
02555                     break;
02556 
02557                 default:
02558                     str = "???";
02559                     type = 0;
02560                     break;
02561             }
02562             Info_SetValueForKey( cl_pinglist[i].info, "nettype", va("%d", type) );
02563             CL_SetServerInfoByAddress(from, infoString, cl_pinglist[i].time);
02564 
02565             return;
02566         }
02567     }
02568 
02569     // if not just sent a local broadcast or pinging local servers
02570     if (cls.pingUpdateSource != AS_LOCAL) {
02571         return;
02572     }
02573 
02574     for ( i = 0 ; i < MAX_OTHER_SERVERS ; i++ ) {
02575         // empty slot
02576         if ( cls.localServers[i].adr.port == 0 ) {
02577             break;
02578         }
02579 
02580         // avoid duplicate
02581         if ( NET_CompareAdr( from, cls.localServers[i].adr ) ) {
02582             return;
02583         }
02584     }
02585 
02586     if ( i == MAX_OTHER_SERVERS ) {
02587         Com_DPrintf( "MAX_OTHER_SERVERS hit, dropping infoResponse\n" );
02588         return;
02589     }
02590 
02591     // add this to the list
02592     cls.numlocalservers = i+1;
02593     cls.localServers[i].adr = from;
02594     cls.localServers[i].clients = 0;
02595     cls.localServers[i].hostName[0] = '\0';
02596     cls.localServers[i].mapName[0] = '\0';
02597     cls.localServers[i].maxClients = 0;
02598     cls.localServers[i].maxPing = 0;
02599     cls.localServers[i].minPing = 0;
02600     cls.localServers[i].ping = -1;
02601     cls.localServers[i].game[0] = '\0';
02602     cls.localServers[i].gameType = 0;
02603     cls.localServers[i].netType = from.type;
02604     cls.localServers[i].punkbuster = 0;
02605                                      
02606     Q_strncpyz( info, MSG_ReadString( msg ), MAX_INFO_STRING );
02607     if (strlen(info)) {
02608         if (info[strlen(info)-1] != '\n') {
02609             strncat(info, "\n", sizeof(info));
02610         }
02611         Com_Printf( "%s: %s", NET_AdrToString( from ), info );
02612     }
02613 }
02614 
02615 /*
02616 ===================
02617 CL_GetServerStatus
02618 ===================
02619 */
02620 serverStatus_t *CL_GetServerStatus( netadr_t from ) {
02621     serverStatus_t *serverStatus;
02622     int i, oldest, oldestTime;
02623 
02624     serverStatus = NULL;
02625     for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
02626         if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
02627             return &cl_serverStatusList[i];
02628         }
02629     }
02630     for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
02631         if ( cl_serverStatusList[i].retrieved ) {
02632             return &cl_serverStatusList[i];
02633         }
02634     }
02635     oldest = -1;
02636     oldestTime = 0;
02637     for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
02638         if (oldest == -1 || cl_serverStatusList[i].startTime < oldestTime) {
02639             oldest = i;
02640             oldestTime = cl_serverStatusList[i].startTime;
02641         }
02642     }
02643     if (oldest != -1) {
02644         return &cl_serverStatusList[oldest];
02645     }
02646     serverStatusCount++;
02647     return &cl_serverStatusList[serverStatusCount & (MAX_SERVERSTATUSREQUESTS-1)];
02648 }
02649 
02650 /*
02651 ===================
02652 CL_ServerStatus
02653 ===================
02654 */
02655 int CL_ServerStatus( char *serverAddress, char *serverStatusString, int maxLen ) {
02656     int i;
02657     netadr_t    to;
02658     serverStatus_t *serverStatus;
02659 
02660     // if no server address then reset all server status requests
02661     if ( !serverAddress ) {
02662         for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
02663             cl_serverStatusList[i].address.port = 0;
02664             cl_serverStatusList[i].retrieved = qtrue;
02665         }
02666         return qfalse;
02667     }
02668     // get the address
02669     if ( !NET_StringToAdr( serverAddress, &to ) ) {
02670         return qfalse;
02671     }
02672     serverStatus = CL_GetServerStatus( to );
02673     // if no server status string then reset the server status request for this address
02674     if ( !serverStatusString ) {
02675         serverStatus->retrieved = qtrue;
02676         return qfalse;
02677     }
02678 
02679     // if this server status request has the same address
02680     if ( NET_CompareAdr( to, serverStatus->address) ) {
02681         // if we recieved an response for this server status request
02682         if (!serverStatus->pending) {
02683             Q_strncpyz(serverStatusString, serverStatus->string, maxLen);
02684             serverStatus->retrieved = qtrue;
02685             serverStatus->startTime = 0;
02686             return qtrue;
02687         }
02688         // resend the request regularly
02689         else if ( serverStatus->startTime < Com_Milliseconds() - cl_serverStatusResendTime->integer ) {
02690             serverStatus->print = qfalse;
02691             serverStatus->pending = qtrue;
02692             serverStatus->retrieved = qfalse;
02693             serverStatus->time = 0;
02694             serverStatus->startTime = Com_Milliseconds();
02695             NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
02696             return qfalse;
02697         }
02698     }
02699     // if retrieved
02700     else if ( serverStatus->retrieved ) {
02701         serverStatus->address = to;
02702         serverStatus->print = qfalse;
02703         serverStatus->pending = qtrue;
02704         serverStatus->retrieved = qfalse;
02705         serverStatus->startTime = Com_Milliseconds();
02706         serverStatus->time = 0;
02707         NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
02708         return qfalse;
02709     }
02710     return qfalse;
02711 }
02712 
02713 /*
02714 ===================
02715 CL_ServerStatusResponse
02716 ===================
02717 */
02718 void CL_ServerStatusResponse( netadr_t from, msg_t *msg ) {
02719     char    *s;
02720     char    info[MAX_INFO_STRING];
02721     int     i, l, score, ping;
02722     int     len;
02723     serverStatus_t *serverStatus;
02724 
02725     serverStatus = NULL;
02726     for (i = 0; i < MAX_SERVERSTATUSREQUESTS; i++) {
02727         if ( NET_CompareAdr( from, cl_serverStatusList[i].address ) ) {
02728             serverStatus = &cl_serverStatusList[i];
02729             break;
02730         }
02731     }
02732     // if we didn't request this server status
02733     if (!serverStatus) {
02734         return;
02735     }
02736 
02737     s = MSG_ReadStringLine( msg );
02738 
02739     len = 0;
02740     Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "%s", s);
02741 
02742     if (serverStatus->print) {
02743         Com_Printf("Server settings:\n");
02744         // print cvars
02745         while (*s) {
02746             for (i = 0; i < 2 && *s; i++) {
02747                 if (*s == '\\')
02748                     s++;
02749                 l = 0;
02750                 while (*s) {
02751                     info[l++] = *s;
02752                     if (l >= MAX_INFO_STRING-1)
02753                         break;
02754                     s++;
02755                     if (*s == '\\') {
02756                         break;
02757                     }
02758                 }
02759                 info[l] = '\0';
02760                 if (i) {
02761                     Com_Printf("%s\n", info);
02762                 }
02763                 else {
02764                     Com_Printf("%-24s", info);
02765                 }
02766             }
02767         }
02768     }
02769 
02770     len = strlen(serverStatus->string);
02771     Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
02772 
02773     if (serverStatus->print) {
02774         Com_Printf("\nPlayers:\n");
02775         Com_Printf("num: score: ping: name:\n");
02776     }
02777     for (i = 0, s = MSG_ReadStringLine( msg ); *s; s = MSG_ReadStringLine( msg ), i++) {
02778 
02779         len = strlen(serverStatus->string);
02780         Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\%s", s);
02781 
02782         if (serverStatus->print) {
02783             score = ping = 0;
02784             sscanf(s, "%d %d", &score, &ping);
02785             s = strchr(s, ' ');
02786             if (s)
02787                 s = strchr(s+1, ' ');
02788             if (s)
02789                 s++;
02790             else
02791                 s = "unknown";
02792             Com_Printf("%-2d   %-3d    %-3d   %s\n", i, score, ping, s );
02793         }
02794     }
02795     len = strlen(serverStatus->string);
02796     Com_sprintf(&serverStatus->string[len], sizeof(serverStatus->string)-len, "\\");
02797 
02798     serverStatus->time = Com_Milliseconds();
02799     serverStatus->address = from;
02800     serverStatus->pending = qfalse;
02801     if (serverStatus->print) {
02802         serverStatus->retrieved = qtrue;
02803     }
02804 }
02805 
02806 /*
02807 ==================
02808 CL_LocalServers_f
02809 ==================
02810 */
02811 void CL_LocalServers_f( void ) {
02812     char        *message;
02813     int         i, j;
02814     netadr_t    to;
02815 
02816     Com_Printf( "Scanning for servers on the local network...\n");
02817 
02818     // reset the list, waiting for response
02819     cls.numlocalservers = 0;
02820     cls.pingUpdateSource = AS_LOCAL;
02821 
02822     for (i = 0; i < MAX_OTHER_SERVERS; i++) {
02823         qboolean b = cls.localServers[i].visible;
02824         Com_Memset(&cls.localServers[i], 0, sizeof(cls.localServers[i]));
02825         cls.localServers[i].visible = b;
02826     }
02827     Com_Memset( &to, 0, sizeof( to ) );
02828 
02829     // The 'xxx' in the message is a challenge that will be echoed back
02830     // by the server.  We don't care about that here, but master servers
02831     // can use that to prevent spoofed server responses from invalid ip
02832     message = "\377\377\377\377getinfo xxx";
02833 
02834     // send each message twice in case one is dropped
02835     for ( i = 0 ; i < 2 ; i++ ) {
02836         // send a broadcast packet on each server port
02837         // we support multiple server ports so a single machine
02838         // can nicely run multiple servers
02839         for ( j = 0 ; j < NUM_SERVER_PORTS ; j++ ) {
02840             to.port = BigShort( (short)(PORT_SERVER + j) );
02841 
02842             to.type = NA_BROADCAST;
02843             NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
02844 
02845             to.type = NA_BROADCAST_IPX;
02846             NET_SendPacket( NS_CLIENT, strlen( message ), message, to );
02847         }
02848     }
02849 }
02850 
02851 /*
02852 ==================
02853 CL_GlobalServers_f
02854 ==================
02855 */
02856 void CL_GlobalServers_f( void ) {
02857     netadr_t    to;
02858     int         i;
02859     int         count;
02860     char        *buffptr;
02861     char        command[1024];
02862     
02863     if ( Cmd_Argc() < 3) {
02864         Com_Printf( "usage: globalservers <master# 0-1> <protocol> [keywords]\n");
02865         return; 
02866     }
02867 
02868     cls.masterNum = atoi( Cmd_Argv(1) );
02869 
02870     Com_Printf( "Requesting servers from the master...\n");
02871 
02872     // reset the list, waiting for response
02873     // -1 is used to distinguish a "no response"
02874 
02875     if( cls.masterNum == 1 ) {
02876         NET_StringToAdr( MASTER_SERVER_NAME, &to );
02877         cls.nummplayerservers = -1;
02878         cls.pingUpdateSource = AS_MPLAYER;
02879     }
02880     else {
02881         NET_StringToAdr( MASTER_SERVER_NAME, &to );
02882         cls.numglobalservers = -1;
02883         cls.pingUpdateSource = AS_GLOBAL;
02884     }
02885     to.type = NA_IP;
02886     to.port = BigShort(PORT_MASTER);
02887 
02888     sprintf( command, "getservers %s", Cmd_Argv(2) );
02889 
02890     // tack on keywords
02891     buffptr = command + strlen( command );
02892     count   = Cmd_Argc();
02893     for (i=3; i<count; i++)
02894         buffptr += sprintf( buffptr, " %s", Cmd_Argv(i) );
02895 
02896     // if we are a demo, automatically add a "demo" keyword
02897     if ( Cvar_VariableValue( "fs_restrict" ) ) {
02898         buffptr += sprintf( buffptr, " demo" );
02899     }
02900 
02901     NET_OutOfBandPrint( NS_SERVER, to, command );
02902 }
02903 
02904 
02905 /*
02906 ==================
02907 CL_GetPing
02908 ==================
02909 */
02910 void CL_GetPing( int n, char *buf, int buflen, int *pingtime )
02911 {
02912     const char  *str;
02913     int     time;
02914     int     maxPing;
02915 
02916     if (!cl_pinglist[n].adr.port)
02917     {
02918         // empty slot
02919         buf[0]    = '\0';
02920         *pingtime = 0;
02921         return;
02922     }
02923 
02924     str = NET_AdrToString( cl_pinglist[n].adr );
02925     Q_strncpyz( buf, str, buflen );
02926 
02927     time = cl_pinglist[n].time;
02928     if (!time)
02929     {
02930         // check for timeout
02931         time = cls.realtime - cl_pinglist[n].start;
02932         maxPing = Cvar_VariableIntegerValue( "cl_maxPing" );
02933         if( maxPing < 100 ) {
02934             maxPing = 100;
02935         }
02936         if (time < maxPing)
02937         {
02938             // not timed out yet
02939             time = 0;
02940         }
02941     }
02942 
02943     CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time);
02944 
02945     *pingtime = time;
02946 }
02947 
02948 /*
02949 ==================
02950 CL_UpdateServerInfo
02951 ==================
02952 */
02953 void CL_UpdateServerInfo( int n )
02954 {
02955     if (!cl_pinglist[n].adr.port)
02956     {
02957         return;
02958     }
02959 
02960     CL_SetServerInfoByAddress(cl_pinglist[n].adr, cl_pinglist[n].info, cl_pinglist[n].time );
02961 }
02962 
02963 /*
02964 ==================
02965 CL_GetPingInfo
02966 ==================
02967 */
02968 void CL_GetPingInfo( int n, char *buf, int buflen )
02969 {
02970     if (!cl_pinglist[n].adr.port)
02971     {
02972         // empty slot
02973         if (buflen)
02974             buf[0] = '\0';
02975         return;
02976     }
02977 
02978     Q_strncpyz( buf, cl_pinglist[n].info, buflen );
02979 }
02980 
02981 /*
02982 ==================
02983 CL_ClearPing
02984 ==================
02985 */
02986 void CL_ClearPing( int n )
02987 {
02988     if (n < 0 || n >= MAX_PINGREQUESTS)
02989         return;
02990 
02991     cl_pinglist[n].adr.port = 0;
02992 }
02993 
02994 /*
02995 ==================
02996 CL_GetPingQueueCount
02997 ==================
02998 */
02999 int CL_GetPingQueueCount( void )
03000 {
03001     int     i;
03002     int     count;
03003     ping_t* pingptr;
03004 
03005     count   = 0;
03006     pingptr = cl_pinglist;
03007 
03008     for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ ) {
03009         if (pingptr->adr.port) {
03010             count++;
03011         }
03012     }
03013 
03014     return (count);
03015 }
03016 
03017 /*
03018 ==================
03019 CL_GetFreePing
03020 ==================
03021 */
03022 ping_t* CL_GetFreePing( void )
03023 {
03024     ping_t* pingptr;
03025     ping_t* best;   
03026     int     oldest;
03027     int     i;
03028     int     time;
03029 
03030     pingptr = cl_pinglist;
03031     for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
03032     {
03033         // find free ping slot
03034         if (pingptr->adr.port)
03035         {
03036             if (!pingptr->time)
03037             {
03038                 if (cls.realtime - pingptr->start < 500)
03039                 {
03040                     // still waiting for response
03041                     continue;
03042                 }
03043             }
03044             else if (pingptr->time < 500)
03045             {
03046                 // results have not been queried
03047                 continue;
03048             }
03049         }
03050 
03051         // clear it
03052         pingptr->adr.port = 0;
03053         return (pingptr);
03054     }
03055 
03056     // use oldest entry
03057     pingptr = cl_pinglist;
03058     best    = cl_pinglist;
03059     oldest  = INT_MIN;
03060     for (i=0; i<MAX_PINGREQUESTS; i++, pingptr++ )
03061     {
03062         // scan for oldest
03063         time = cls.realtime - pingptr->start;
03064         if (time > oldest)
03065         {
03066             oldest = time;
03067             best   = pingptr;
03068         }
03069     }
03070 
03071     return (best);
03072 }
03073 
03074 /*
03075 ==================
03076 CL_Ping_f
03077 ==================
03078 */
03079 void CL_Ping_f( void ) {
03080     netadr_t    to;
03081     ping_t*     pingptr;
03082     char*       server;
03083 
03084     if ( Cmd_Argc() != 2 ) {
03085         Com_Printf( "usage: ping [server]\n");
03086         return; 
03087     }
03088 
03089     Com_Memset( &to, 0, sizeof(netadr_t) );
03090 
03091     server = Cmd_Argv(1);
03092 
03093     if ( !NET_StringToAdr( server, &to ) ) {
03094         return;
03095     }
03096 
03097     pingptr = CL_GetFreePing();
03098 
03099     memcpy( &pingptr->adr, &to, sizeof (netadr_t) );
03100     pingptr->start = cls.realtime;
03101     pingptr->time  = 0;
03102 
03103     CL_SetServerInfoByAddress(pingptr->adr, NULL, 0);
03104         
03105     NET_OutOfBandPrint( NS_CLIENT, to, "getinfo xxx" );
03106 }
03107 
03108 /*
03109 ==================
03110 CL_UpdateVisiblePings_f
03111 ==================
03112 */
03113 qboolean CL_UpdateVisiblePings_f(int source) {
03114     int         slots, i;
03115     char        buff[MAX_STRING_CHARS];
03116     int         pingTime;
03117     int         max;
03118     qboolean status = qfalse;
03119 
03120     if (source < 0 || source > AS_FAVORITES) {
03121         return qfalse;
03122     }
03123 
03124     cls.pingUpdateSource = source;
03125 
03126     slots = CL_GetPingQueueCount();
03127     if (slots < MAX_PINGREQUESTS) {
03128         serverInfo_t *server = NULL;
03129 
03130         max = (source == AS_GLOBAL) ? MAX_GLOBAL_SERVERS : MAX_OTHER_SERVERS;
03131         switch (source) {
03132             case AS_LOCAL :
03133                 server = &cls.localServers[0];
03134                 max = cls.numlocalservers;
03135             break;
03136             case AS_MPLAYER :
03137                 server = &cls.mplayerServers[0];
03138                 max = cls.nummplayerservers;
03139             break;
03140             case AS_GLOBAL :
03141                 server = &cls.globalServers[0];
03142                 max = cls.numglobalservers;
03143             break;
03144             case AS_FAVORITES :
03145                 server = &cls.favoriteServers[0];
03146                 max = cls.numfavoriteservers;
03147             break;
03148         }
03149         for (i = 0; i < max; i++) {
03150             if (server[i].visible) {
03151                 if (server[i].ping == -1) {
03152                     int j;
03153 
03154                     if (slots >= MAX_PINGREQUESTS) {
03155                         break;
03156                     }
03157                     for (j = 0; j < MAX_PINGREQUESTS; j++) {
03158                         if (!cl_pinglist[j].adr.port) {
03159                             continue;
03160                         }
03161                         if (NET_CompareAdr( cl_pinglist[j].adr, server[i].adr)) {
03162                             // already on the list
03163                             break;
03164                         }
03165                     }
03166                     if (j >= MAX_PINGREQUESTS) {
03167                         status = qtrue;
03168                         for (j = 0; j < MAX_PINGREQUESTS; j++) {
03169                             if (!cl_pinglist[j].adr.port) {
03170                                 break;
03171                             }
03172                         }
03173                         memcpy(&cl_pinglist[j].adr, &server[i].adr, sizeof(netadr_t));
03174                         cl_pinglist[j].start = cls.realtime;
03175                         cl_pinglist[j].time = 0;
03176                         NET_OutOfBandPrint( NS_CLIENT, cl_pinglist[j].adr, "getinfo xxx" );
03177                         slots++;
03178                     }
03179                 }
03180                 // if the server has a ping higher than cl_maxPing or
03181                 // the ping packet got lost
03182                 else if (server[i].ping == 0) {
03183                     // if we are updating global servers
03184                     if (source == AS_GLOBAL) {
03185                         //
03186                         if ( cls.numGlobalServerAddresses > 0 ) {
03187                             // overwrite this server with one from the additional global servers
03188                             cls.numGlobalServerAddresses--;
03189                             CL_InitServerInfo(&server[i], &cls.globalServerAddresses[cls.numGlobalServerAddresses]);
03190                             // NOTE: the server[i].visible flag stays untouched
03191                         }
03192                     }
03193                 }
03194             }
03195         }
03196     } 
03197 
03198     if (slots) {
03199         status = qtrue;
03200     }
03201     for (i = 0; i < MAX_PINGREQUESTS; i++) {
03202         if (!cl_pinglist[i].adr.port) {
03203             continue;
03204         }
03205         CL_GetPing( i, buff, MAX_STRING_CHARS, &pingTime );
03206         if (pingTime != 0) {
03207             CL_ClearPing(i);
03208             status = qtrue;
03209         }
03210     }
03211 
03212     return status;
03213 }
03214 
03215 /*
03216 ==================
03217 CL_ServerStatus_f
03218 ==================
03219 */
03220 void CL_ServerStatus_f(void) {
03221     netadr_t    to;
03222     char        *server;
03223     serverStatus_t *serverStatus;
03224 
03225     Com_Memset( &to, 0, sizeof(netadr_t) );
03226 
03227     if ( Cmd_Argc() != 2 ) {
03228         if ( cls.state != CA_ACTIVE || clc.demoplaying ) {
03229             Com_Printf ("Not connected to a server.\n");
03230             Com_Printf( "Usage: serverstatus [server]\n");
03231             return; 
03232         }
03233         server = cls.servername;
03234     }
03235     else {
03236         server = Cmd_Argv(1);
03237     }
03238 
03239     if ( !NET_StringToAdr( server, &to ) ) {
03240         return;
03241     }
03242 
03243     NET_OutOfBandPrint( NS_CLIENT, to, "getstatus" );
03244 
03245     serverStatus = CL_GetServerStatus( to );
03246     serverStatus->address = to;
03247     serverStatus->print = qtrue;
03248     serverStatus->pending = qtrue;
03249 }
03250 
03251 /*
03252 ==================
03253 CL_ShowIP_f
03254 ==================
03255 */
03256 void CL_ShowIP_f(void) {
03257     Sys_ShowIP();
03258 }
03259 
03260 /*
03261 =================
03262 bool CL_CDKeyValidate
03263 =================
03264 */
03265 qboolean CL_CDKeyValidate( const char *key, const char *checksum ) {
03266     char    ch;
03267     byte    sum;
03268     char    chs[3];
03269     int i, len;
03270 
03271     len = strlen(key);
03272     if( len != CDKEY_LEN ) {
03273         return qfalse;
03274     }
03275 
03276     if( checksum && strlen( checksum ) != CDCHKSUM_LEN ) {
03277         return qfalse;
03278     }
03279 
03280     sum = 0;
03281     // for loop gets rid of conditional assignment warning
03282     for (i = 0; i < len; i++) {
03283         ch = *key++;
03284         if (ch>='a' && ch<='z') {
03285             ch -= 32;
03286         }
03287         switch( ch ) {
03288         case '2':
03289         case '3':
03290         case '7':
03291         case 'A':
03292         case 'B':
03293         case 'C':
03294         case 'D':
03295         case 'G':
03296         case 'H':
03297         case 'J':
03298         case 'L':
03299         case 'P':
03300         case 'R':
03301         case 'S':
03302         case 'T':
03303         case 'W':
03304             sum += ch;
03305             continue;
03306         default:
03307             return qfalse;
03308         }
03309     }
03310 
03311     sprintf(chs, "%02x", sum);
03312     
03313     if (checksum && !Q_stricmp(chs, checksum)) {
03314         return qtrue;
03315     }
03316 
03317     if (!checksum) {
03318         return qtrue;
03319     }
03320 
03321     return qfalse;
03322 }
03323 
03324 

Generated on Thu Aug 25 12:37:27 2005 for Quake III Arena by  doxygen 1.3.9.1