File: client\cl_main.c

    1 /*
    2 Copyright (C) 1997-2001 Id Software, Inc.
    3 
    4 This program is free software; you can redistribute it and/or
    5 modify it under the terms of the GNU General Public License
    6 as published by the Free Software Foundation; either version 2
    7 of the License, or (at your option) any later version.
    8 
    9 This program is distributed in the hope that it will be useful,
   10 but WITHOUT ANY WARRANTY; without even the implied warranty of
   11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
   12 
   13 See the GNU General Public License for more details.
   14 
   15 You should have received a copy of the GNU General Public License
   16 along with this program; if not, write to the Free Software
   17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
   18 
   19 */
   20 // cl_main.c  -- client main loop
   21 
   22 #include "client.h"
   23 
   24 cvar_t  *freelook;
   25 
   26 cvar_t  *adr0;
   27 cvar_t  *adr1;
   28 cvar_t  *adr2;
   29 cvar_t  *adr3;
   30 cvar_t  *adr4;
   31 cvar_t  *adr5;
   32 cvar_t  *adr6;
   33 cvar_t  *adr7;
   34 cvar_t  *adr8;
   35 
   36 cvar_t  *cl_stereo_separation;
   37 cvar_t  *cl_stereo;
   38 
   39 cvar_t  *rcon_client_password;
   40 cvar_t  *rcon_address;
   41 
   42 cvar_t  *cl_noskins;
   43 cvar_t  *cl_autoskins;
   44 cvar_t  *cl_footsteps;
   45 cvar_t  *cl_timeout;
   46 cvar_t  *cl_predict;
   47 //cvar_t        *cl_minfps;
   48 cvar_t  *cl_maxfps;
   49 cvar_t  *cl_gun;
   50 
   51 cvar_t  *cl_add_particles;
   52 cvar_t  *cl_add_lights;
   53 cvar_t  *cl_add_entities;
   54 cvar_t  *cl_add_blend;
   55 
   56 cvar_t  *cl_shownet;
   57 cvar_t  *cl_showmiss;
   58 cvar_t  *cl_showclamp;
   59 
   60 cvar_t  *cl_paused;
   61 cvar_t  *cl_timedemo;
   62 
   63 cvar_t  *lookspring;
   64 cvar_t  *lookstrafe;
   65 cvar_t  *sensitivity;
   66 
   67 cvar_t  *m_pitch;
   68 cvar_t  *m_yaw;
   69 cvar_t  *m_forward;
   70 cvar_t  *m_side;
   71 
   72 cvar_t  *cl_lightlevel;
   73 
   74 //
   75 // userinfo
   76 //
   77 cvar_t  *info_password;
   78 cvar_t  *info_spectator;
   79 cvar_t  *name;
   80 cvar_t  *skin;
   81 cvar_t  *rate;
   82 cvar_t  *fov;
   83 cvar_t  *msg;
   84 cvar_t  *hand;
   85 cvar_t  *gender;
   86 cvar_t  *gender_auto;
   87 
   88 cvar_t  *cl_vwep;
   89 
   90 client_static_t cls;
   91 client_state_t  cl;
   92 
   93 centity_t               cl_entities[MAX_EDICTS];
   94 
   95 entity_state_t  cl_parse_entities[MAX_PARSE_ENTITIES];
   96 
   97 extern  cvar_t *allow_download;
   98 extern  cvar_t *allow_download_players;
   99 extern  cvar_t *allow_download_models;
  100 extern  cvar_t *allow_download_sounds;
  101 extern  cvar_t *allow_download_maps;
  102 
  103 //======================================================================
  104 
  105 
  106 /*
  107 ====================
  108 CL_WriteDemoMessage
  109 
  110 Dumps the current net message, prefixed by the length
  111 ====================
  112 */
  113 void CL_WriteDemoMessage (void)
  114 {
  115         int             len, swlen;
  116 
  117         // the first eight bytes are just packet sequencing stuff
  118         len = net_message.cursize-8;
  119         swlen = LittleLong(len);
  120         fwrite (&swlen, 4, 1, cls.demofile);
  121         fwrite (net_message.data+8,     len, 1, cls.demofile);
  122 }
  123 
  124 
  125 /*
  126 ====================
  127 CL_Stop_f
  128 
  129 stop recording a demo
  130 ====================
  131 */
  132 void CL_Stop_f (void)
  133 {
  134         int             len;
  135 
  136         if (!cls.demorecording)
  137         {
  138                 Com_Printf ("Not recording a demo.\n");
  139                 return;
  140         }
  141 
  142 // finish up
  143         len = -1;
  144         fwrite (&len, 4, 1, cls.demofile);
  145         fclose (cls.demofile);
  146         cls.demofile = NULL;
  147         cls.demorecording = false;
  148         Com_Printf ("Stopped demo.\n");
  149 }
  150 
  151 /*
  152 ====================
  153 CL_Record_f
  154 
  155 record <demoname>
  156 
  157 Begins recording a demo from the current position
  158 ====================
  159 */
  160 void CL_Record_f (void)
  161 {
  162         char    name[MAX_OSPATH];
  163         char    buf_data[MAX_MSGLEN];
  164         sizebuf_t       buf;
  165         int             i;
  166         int             len;
  167         entity_state_t  *ent;
  168         entity_state_t  nullstate;
  169 
  170         if (Cmd_Argc() != 2)
  171         {
  172                 Com_Printf ("record <demoname>\n");
  173                 return;
  174         }
  175 
  176         if (cls.demorecording)
  177         {
  178                 Com_Printf ("Already recording.\n");
  179                 return;
  180         }
  181 
  182         if (cls.state != ca_active)
  183         {
  184                 Com_Printf ("You must be in a level to record.\n");
  185                 return;
  186         }
  187 
  188         //
  189         // open the demo file
  190         //
  191         Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
  192 
  193         Com_Printf ("recording to %s.\n", name);
  194         FS_CreatePath (name);
  195         cls.demofile = fopen (name, "wb");
  196         if (!cls.demofile)
  197         {
  198                 Com_Printf ("ERROR: couldn't open.\n");
  199                 return;
  200         }
  201         cls.demorecording = true;
  202 
  203         // don't start saving messages until a non-delta compressed message is received
  204         cls.demowaiting = true;
  205 
  206         //
  207         // write out messages to hold the startup information
  208         //
  209         SZ_Init (&buf, buf_data, sizeof(buf_data));
  210 
  211         // send the serverdata
  212         MSG_WriteByte (&buf, svc_serverdata);
  213         MSG_WriteLong (&buf, PROTOCOL_VERSION);
  214         MSG_WriteLong (&buf, 0x10000 + cl.servercount);
  215         MSG_WriteByte (&buf, 1);        // demos are always attract loops
  216         MSG_WriteString (&buf, cl.gamedir);
  217         MSG_WriteShort (&buf, cl.playernum);
  218 
  219         MSG_WriteString (&buf, cl.configstrings[CS_NAME]);
  220 
  221         // configstrings
  222         for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
  223         {
  224                 if (cl.configstrings[i][0])
  225                 {
  226                         if (buf.cursize + strlen (cl.configstrings[i]) + 32 > buf.maxsize)
  227                         {       // write it out
  228                                 len = LittleLong (buf.cursize);
  229                                 fwrite (&len, 4, 1, cls.demofile);
  230                                 fwrite (buf.data, buf.cursize, 1, cls.demofile);
  231                                 buf.cursize = 0;
  232                         }
  233 
  234                         MSG_WriteByte (&buf, svc_configstring);
  235                         MSG_WriteShort (&buf, i);
  236                         MSG_WriteString (&buf, cl.configstrings[i]);
  237                 }
  238 
  239         }
  240 
  241         // baselines
  242         memset (&nullstate, 0, sizeof(nullstate));
  243         for (i=0; i<MAX_EDICTS ; i++)
  244         {
  245                 ent = &cl_entities[i].baseline;
  246                 if (!ent->modelindex)
  247                         continue;
  248 
  249                 if (buf.cursize + 64 > buf.maxsize)
  250                 {       // write it out
  251                         len = LittleLong (buf.cursize);
  252                         fwrite (&len, 4, 1, cls.demofile);
  253                         fwrite (buf.data, buf.cursize, 1, cls.demofile);
  254                         buf.cursize = 0;
  255                 }
  256 
  257                 MSG_WriteByte (&buf, svc_spawnbaseline);                
  258                 MSG_WriteDeltaEntity (&nullstate, &cl_entities[i].baseline, &buf, true, true);
  259         }
  260 
  261         MSG_WriteByte (&buf, svc_stufftext);
  262         MSG_WriteString (&buf, "precache\n");
  263 
  264         // write it to the demo file
  265 
  266         len = LittleLong (buf.cursize);
  267         fwrite (&len, 4, 1, cls.demofile);
  268         fwrite (buf.data, buf.cursize, 1, cls.demofile);
  269 
  270         // the rest of the demo file will be individual frames
  271 }
  272 
  273 //======================================================================
  274 
  275 /*
  276 ===================
  277 Cmd_ForwardToServer
  278 
  279 adds the current command line as a clc_stringcmd to the client message.
  280 things like godmode, noclip, etc, are commands directed to the server,
  281 so when they are typed in at the console, they will need to be forwarded.
  282 ===================
  283 */
  284 void Cmd_ForwardToServer (void)
  285 {
  286         char    *cmd;
  287 
  288         cmd = Cmd_Argv(0);
  289         if (cls.state <= ca_connected || *cmd == '-' || *cmd == '+')
  290         {
  291                 Com_Printf ("Unknown command \"%s\"\n", cmd);
  292                 return;
  293         }
  294 
  295         MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
  296         SZ_Print (&cls.netchan.message, cmd);
  297         if (Cmd_Argc() > 1)
  298         {
  299                 SZ_Print (&cls.netchan.message, " ");
  300                 SZ_Print (&cls.netchan.message, Cmd_Args());
  301         }
  302 }
  303 
  304 void CL_Setenv_f( void )
  305 {
  306         int argc = Cmd_Argc();
  307 
  308         if ( argc > 2 )
  309         {
  310                 char buffer[1000];
  311                 int i;
  312 
  313                 strcpy( buffer, Cmd_Argv(1) );
  314                 strcat( buffer, "=" );
  315 
  316                 for ( i = 2; i < argc; i++ )
  317                 {
  318                         strcat( buffer, Cmd_Argv( i ) );
  319                         strcat( buffer, " " );
  320                 }
  321 
  322                 putenv( buffer );
  323         }
  324         else if ( argc == 2 )
  325         {
  326                 char *env = getenv( Cmd_Argv(1) );
  327 
  328                 if ( env )
  329                 {
  330                         Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
  331                 }
  332                 else
  333                 {
  334                         Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
  335                 }
  336         }
  337 }
  338 
  339 
  340 /*
  341 ==================
  342 CL_ForwardToServer_f
  343 ==================
  344 */
  345 void CL_ForwardToServer_f (void)
  346 {
  347         if (cls.state != ca_connected && cls.state != ca_active)
  348         {
  349                 Com_Printf ("Can't \"%s\", not connected\n", Cmd_Argv(0));
  350                 return;
  351         }
  352         
  353         // don't forward the first argument
  354         if (Cmd_Argc() > 1)
  355         {
  356                 MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
  357                 SZ_Print (&cls.netchan.message, Cmd_Args());
  358         }
  359 }
  360 
  361 
  362 /*
  363 ==================
  364 CL_Pause_f
  365 ==================
  366 */
  367 void CL_Pause_f (void)
  368 {
  369         // never pause in multiplayer
  370         if (Cvar_VariableValue ("maxclients") > 1 || !Com_ServerState ())
  371         {
  372                 Cvar_SetValue ("paused", 0);
  373                 return;
  374         }
  375 
  376         Cvar_SetValue ("paused", !cl_paused->value);
  377 }
  378 
  379 /*
  380 ==================
  381 CL_Quit_f
  382 ==================
  383 */
  384 void CL_Quit_f (void)
  385 {
  386         CL_Disconnect ();
  387         Com_Quit ();
  388 }
  389 
  390 /*
  391 ================
  392 CL_Drop
  393 
  394 Called after an ERR_DROP was thrown
  395 ================
  396 */
  397 void CL_Drop (void)
  398 {
  399         if (cls.state == ca_uninitialized)
  400                 return;
  401         if (cls.state == ca_disconnected)
  402                 return;
  403 
  404         CL_Disconnect ();
  405 
  406         // drop loading plaque unless this is the initial game start
  407         if (cls.disable_servercount != -1)
  408                 SCR_EndLoadingPlaque ();        // get rid of loading plaque
  409 }
  410 
  411 
  412 /*
  413 =======================
  414 CL_SendConnectPacket
  415 
  416 We have gotten a challenge from the server, so try and
  417 connect.
  418 ======================
  419 */
  420 void CL_SendConnectPacket (void)
  421 {
  422         netadr_t        adr;
  423         int             port;
  424 
  425         if (!NET_StringToAdr (cls.servername, &adr))
  426         {
  427                 Com_Printf ("Bad server address\n");
  428                 cls.connect_time = 0;
  429                 return;
  430         }
  431         if (adr.port == 0)
  432                 adr.port = BigShort (PORT_SERVER);
  433 
  434         port = Cvar_VariableValue ("qport");
  435         userinfo_modified = false;
  436 
  437         Netchan_OutOfBandPrint (NS_CLIENT, adr, "connect %i %i %i \"%s\"\n",
  438                 PROTOCOL_VERSION, port, cls.challenge, Cvar_Userinfo() );
  439 }
  440 
  441 /*
  442 =================
  443 CL_CheckForResend
  444 
  445 Resend a connect message if the last one has timed out
  446 =================
  447 */
  448 void CL_CheckForResend (void)
  449 {
  450         netadr_t        adr;
  451 
  452         // if the local server is running and we aren't
  453         // then connect
  454         if (cls.state == ca_disconnected && Com_ServerState() )
  455         {
  456                 cls.state = ca_connecting;
  457                 strncpy (cls.servername, "localhost", sizeof(cls.servername)-1);
  458                 // we don't need a challenge on the localhost
  459                 CL_SendConnectPacket ();
  460                 return;
  461 //              cls.connect_time = -99999;      // CL_CheckForResend() will fire immediately
  462         }
  463 
  464         // resend if we haven't gotten a reply yet
  465         if (cls.state != ca_connecting)
  466                 return;
  467 
  468         if (cls.realtime - cls.connect_time < 3000)
  469                 return;
  470 
  471         if (!NET_StringToAdr (cls.servername, &adr))
  472         {
  473                 Com_Printf ("Bad server address\n");
  474                 cls.state = ca_disconnected;
  475                 return;
  476         }
  477         if (adr.port == 0)
  478                 adr.port = BigShort (PORT_SERVER);
  479 
  480         cls.connect_time = cls.realtime;        // for retransmit requests
  481 
  482         Com_Printf ("Connecting to %s...\n", cls.servername);
  483 
  484         Netchan_OutOfBandPrint (NS_CLIENT, adr, "getchallenge\n");
  485 }
  486 
  487 
  488 /*
  489 ================
  490 CL_Connect_f
  491 
  492 ================
  493 */
  494 void CL_Connect_f (void)
  495 {
  496         char    *server;
  497 
  498         if (Cmd_Argc() != 2)
  499         {
  500                 Com_Printf ("usage: connect <server>\n");
  501                 return; 
  502         }
  503         
  504         if (Com_ServerState ())
  505         {       // if running a local server, kill it and reissue
  506                 SV_Shutdown (va("Server quit\n", msg), false);
  507         }
  508         else
  509         {
  510                 CL_Disconnect ();
  511         }
  512 
  513         server = Cmd_Argv (1);
  514 
  515         NET_Config (true);              // allow remote
  516 
  517         CL_Disconnect ();
  518 
  519         cls.state = ca_connecting;
  520         strncpy (cls.servername, server, sizeof(cls.servername)-1);
  521         cls.connect_time = -99999;      // CL_CheckForResend() will fire immediately
  522 }
  523 
  524 
  525 /*
  526 =====================
  527 CL_Rcon_f
  528 
  529   Send the rest of the command line over as
  530   an unconnected command.
  531 =====================
  532 */
  533 void CL_Rcon_f (void)
  534 {
  535         char    message[1024];
  536         int             i;
  537         netadr_t        to;
  538 
  539         if (!rcon_client_password->string)
  540         {
  541                 Com_Printf ("You must set 'rcon_password' before\n"
  542                                         "issuing an rcon command.\n");
  543                 return;
  544         }
  545 
  546         message[0] = (char)255;
  547         message[1] = (char)255;
  548         message[2] = (char)255;
  549         message[3] = (char)255;
  550         message[4] = 0;
  551 
  552         NET_Config (true);              // allow remote
  553 
  554         strcat (message, "rcon ");
  555 
  556         strcat (message, rcon_client_password->string);
  557         strcat (message, " ");
  558 
  559         for (i=1 ; i<Cmd_Argc() ; i++)
  560         {
  561                 strcat (message, Cmd_Argv(i));
  562                 strcat (message, " ");
  563         }
  564 
  565         if (cls.state >= ca_connected)
  566                 to = cls.netchan.remote_address;
  567         else
  568         {
  569                 if (!strlen(rcon_address->string))
  570                 {
  571                         Com_Printf ("You must either be connected,\n"
  572                                                 "or set the 'rcon_address' cvar\n"
  573                                                 "to issue rcon commands\n");
  574 
  575                         return;
  576                 }
  577                 NET_StringToAdr (rcon_address->string, &to);
  578                 if (to.port == 0)
  579                         to.port = BigShort (PORT_SERVER);
  580         }
  581         
  582         NET_SendPacket (NS_CLIENT, strlen(message)+1, message, to);
  583 }
  584 
  585 
  586 /*
  587 =====================
  588 CL_ClearState
  589 
  590 =====================
  591 */
  592 void CL_ClearState (void)
  593 {
  594         S_StopAllSounds ();
  595         CL_ClearEffects ();
  596         CL_ClearTEnts ();
  597 
  598 // wipe the entire cl structure
  599         memset (&cl, 0, sizeof(cl));
  600         memset (&cl_entities, 0, sizeof(cl_entities));
  601 
  602         SZ_Clear (&cls.netchan.message);
  603 
  604 }
  605 
  606 /*
  607 =====================
  608 CL_Disconnect
  609 
  610 Goes from a connected state to full screen console state
  611 Sends a disconnect message to the server
  612 This is also called on Com_Error, so it shouldn't cause any errors
  613 =====================
  614 */
  615 void CL_Disconnect (void)
  616 {
  617         byte    final[32];
  618 
  619         if (cls.state == ca_disconnected)
  620                 return;
  621 
  622         if (cl_timedemo && cl_timedemo->value)
  623         {
  624                 int     time;
  625                 
  626                 time = Sys_Milliseconds () - cl.timedemo_start;
  627                 if (time > 0)
  628                         Com_Printf ("%i frames, %3.1f seconds: %3.1f fps\n", cl.timedemo_frames,
  629                         time/1000.0, cl.timedemo_frames*1000.0 / time);
  630         }
  631 
  632         VectorClear (cl.refdef.blend);
  633         re.CinematicSetPalette(NULL);
  634 
  635         M_ForceMenuOff ();
  636 
  637         cls.connect_time = 0;
  638 
  639         SCR_StopCinematic ();
  640 
  641         if (cls.demorecording)
  642                 CL_Stop_f ();
  643 
  644         // send a disconnect message to the server
  645         final[0] = clc_stringcmd;
  646         strcpy ((char *)final+1, "disconnect");
  647         Netchan_Transmit (&cls.netchan, strlen(final), final);
  648         Netchan_Transmit (&cls.netchan, strlen(final), final);
  649         Netchan_Transmit (&cls.netchan, strlen(final), final);
  650 
  651         CL_ClearState ();
  652 
  653         // stop download
  654         if (cls.download) {
  655                 fclose(cls.download);
  656                 cls.download = NULL;
  657         }
  658 
  659         cls.state = ca_disconnected;
  660 }
  661 
  662 void CL_Disconnect_f (void)
  663 {
  664         Com_Error (ERR_DROP, "Disconnected from server");
  665 }
  666 
  667 
  668 /*
  669 ====================
  670 CL_Packet_f
  671 
  672 packet <destination> <contents>
  673 
  674 Contents allows \n escape character
  675 ====================
  676 */
  677 void CL_Packet_f (void)
  678 {
  679         char    send[2048];
  680         int             i, l;
  681         char    *in, *out;
  682         netadr_t        adr;
  683 
  684         if (Cmd_Argc() != 3)
  685         {
  686                 Com_Printf ("packet <destination> <contents>\n");
  687                 return;
  688         }
  689 
  690         NET_Config (true);              // allow remote
  691 
  692         if (!NET_StringToAdr (Cmd_Argv(1), &adr))
  693         {
  694                 Com_Printf ("Bad address\n");
  695                 return;
  696         }
  697         if (!adr.port)
  698                 adr.port = BigShort (PORT_SERVER);
  699 
  700         in = Cmd_Argv(2);
  701         out = send+4;
  702         send[0] = send[1] = send[2] = send[3] = (char)0xff;
  703 
  704         l = strlen (in);
  705         for (i=0 ; i<l ; i++)
  706         {
  707                 if (in[i] == '\\' && in[i+1] == 'n')
  708                 {
  709                         *out++ = '\n';
  710                         i++;
  711                 }
  712                 else
  713                         *out++ = in[i];
  714         }
  715         *out = 0;
  716 
  717         NET_SendPacket (NS_CLIENT, out-send, send, adr);
  718 }
  719 
  720 /*
  721 =================
  722 CL_Changing_f
  723 
  724 Just sent as a hint to the client that they should
  725 drop to full console
  726 =================
  727 */
  728 void CL_Changing_f (void)
  729 {
  730         //ZOID
  731         //if we are downloading, we don't change!  This so we don't suddenly stop downloading a map
  732         if (cls.download)
  733                 return;
  734 
  735         SCR_BeginLoadingPlaque ();
  736         cls.state = ca_connected;       // not active anymore, but not disconnected
  737         Com_Printf ("\nChanging map...\n");
  738 }
  739 
  740 
  741 /*
  742 =================
  743 CL_Reconnect_f
  744 
  745 The server is changing levels
  746 =================
  747 */
  748 void CL_Reconnect_f (void)
  749 {
  750         //ZOID
  751         //if we are downloading, we don't change!  This so we don't suddenly stop downloading a map
  752         if (cls.download)
  753                 return;
  754 
  755         S_StopAllSounds ();
  756         if (cls.state == ca_connected) {
  757                 Com_Printf ("reconnecting...\n");
  758                 cls.state = ca_connected;
  759                 MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
  760                 MSG_WriteString (&cls.netchan.message, "new");          
  761                 return;
  762         }
  763 
  764         if (*cls.servername) {
  765                 if (cls.state >= ca_connected) {
  766                         CL_Disconnect();
  767                         cls.connect_time = cls.realtime - 1500;
  768                 } else
  769                         cls.connect_time = -99999; // fire immediately
  770 
  771                 cls.state = ca_connecting;
  772                 Com_Printf ("reconnecting...\n");
  773         }
  774 }
  775 
  776 /*
  777 =================
  778 CL_ParseStatusMessage
  779 
  780 Handle a reply from a ping
  781 =================
  782 */
  783 void CL_ParseStatusMessage (void)
  784 {
  785         char    *s;
  786 
  787         s = MSG_ReadString(&net_message);
  788 
  789         Com_Printf ("%s\n", s);
  790         M_AddToServerList (net_from, s);
  791 }
  792 
  793 
  794 /*
  795 =================
  796 CL_PingServers_f
  797 =================
  798 */
  799 void CL_PingServers_f (void)
  800 {
  801         int                     i;
  802         netadr_t        adr;
  803         char            name[32];
  804         char            *adrstring;
  805         cvar_t          *noudp;
  806         cvar_t          *noipx;
  807 
  808         NET_Config (true);              // allow remote
  809 
  810         // send a broadcast packet
  811         Com_Printf ("pinging broadcast...\n");
  812 
  813         noudp = Cvar_Get ("noudp", "0", CVAR_NOSET);
  814         if (!noudp->value)
  815         {
  816                 adr.type = NA_BROADCAST;
  817                 adr.port = BigShort(PORT_SERVER);
  818                 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
  819         }
  820 
  821         noipx = Cvar_Get ("noipx", "0", CVAR_NOSET);
  822         if (!noipx->value)
  823         {
  824                 adr.type = NA_BROADCAST_IPX;
  825                 adr.port = BigShort(PORT_SERVER);
  826                 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
  827         }
  828 
  829         // send a packet to each address book entry
  830         for (i=0 ; i<16 ; i++)
  831         {
  832                 Com_sprintf (name, sizeof(name), "adr%i", i);
  833                 adrstring = Cvar_VariableString (name);
  834                 if (!adrstring || !adrstring[0])
  835                         continue;
  836 
  837                 Com_Printf ("pinging %s...\n", adrstring);
  838                 if (!NET_StringToAdr (adrstring, &adr))
  839                 {
  840                         Com_Printf ("Bad address: %s\n", adrstring);
  841                         continue;
  842                 }
  843                 if (!adr.port)
  844                         adr.port = BigShort(PORT_SERVER);
  845                 Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_VERSION));
  846         }
  847 }
  848 
  849 
  850 /*
  851 =================
  852 CL_Skins_f
  853 
  854 Load or download any custom player skins and models
  855 =================
  856 */
  857 void CL_Skins_f (void)
  858 {
  859         int             i;
  860 
  861         for (i=0 ; i<MAX_CLIENTS ; i++)
  862         {
  863                 if (!cl.configstrings[CS_PLAYERSKINS+i][0])
  864                         continue;
  865                 Com_Printf ("client %i: %s\n", i, cl.configstrings[CS_PLAYERSKINS+i]); 
  866                 SCR_UpdateScreen ();
  867                 Sys_SendKeyEvents ();   // pump message loop
  868                 CL_ParseClientinfo (i);
  869         }
  870 }
  871 
  872 
  873 /*
  874 =================
  875 CL_ConnectionlessPacket
  876 
  877 Responses to broadcasts, etc
  878 =================
  879 */
  880 void CL_ConnectionlessPacket (void)
  881 {
  882         char    *s;
  883         char    *c;
  884         
  885         MSG_BeginReading (&net_message);
  886         MSG_ReadLong (&net_message);    // skip the -1
  887 
  888         s = MSG_ReadStringLine (&net_message);
  889 
  890         Cmd_TokenizeString (s, false);
  891 
  892         c = Cmd_Argv(0);
  893 
  894         Com_Printf ("%s: %s\n", NET_AdrToString (net_from), c);
  895 
  896         // server connection
  897         if (!strcmp(c, "client_connect"))
  898         {
  899                 if (cls.state == ca_connected)
  900                 {
  901                         Com_Printf ("Dup connect received.  Ignored.\n");
  902                         return;
  903                 }
  904                 Netchan_Setup (NS_CLIENT, &cls.netchan, net_from, cls.quakePort);
  905                 MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
  906                 MSG_WriteString (&cls.netchan.message, "new");  
  907                 cls.state = ca_connected;
  908                 return;
  909         }
  910 
  911         // server responding to a status broadcast
  912         if (!strcmp(c, "info"))
  913         {
  914                 CL_ParseStatusMessage ();
  915                 return;
  916         }
  917 
  918         // remote command from gui front end
  919         if (!strcmp(c, "cmd"))
  920         {
  921                 if (!NET_IsLocalAddress(net_from))
  922                 {
  923                         Com_Printf ("Command packet from remote host.  Ignored.\n");
  924                         return;
  925                 }
  926                 Sys_AppActivate ();
  927                 s = MSG_ReadString (&net_message);
  928                 Cbuf_AddText (s);
  929                 Cbuf_AddText ("\n");
  930                 return;
  931         }
  932         // print command from somewhere
  933         if (!strcmp(c, "print"))
  934         {
  935                 s = MSG_ReadString (&net_message);
  936                 Com_Printf ("%s", s);
  937                 return;
  938         }
  939 
  940         // ping from somewhere
  941         if (!strcmp(c, "ping"))
  942         {
  943                 Netchan_OutOfBandPrint (NS_CLIENT, net_from, "ack");
  944                 return;
  945         }
  946 
  947         // challenge from the server we are connecting to
  948         if (!strcmp(c, "challenge"))
  949         {
  950                 cls.challenge = atoi(Cmd_Argv(1));
  951                 CL_SendConnectPacket ();
  952                 return;
  953         }
  954 
  955         // echo request from server
  956         if (!strcmp(c, "echo"))
  957         {
  958                 Netchan_OutOfBandPrint (NS_CLIENT, net_from, "%s", Cmd_Argv(1) );
  959                 return;
  960         }
  961 
  962         Com_Printf ("Unknown command.\n");
  963 }
  964 
  965 
  966 /*
  967 =================
  968 CL_DumpPackets
  969 
  970 A vain attempt to help bad TCP stacks that cause problems
  971 when they overflow
  972 =================
  973 */
  974 void CL_DumpPackets (void)
  975 {
  976         while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
  977         {
  978                 Com_Printf ("dumnping a packet\n");
  979         }
  980 }
  981 
  982 /*
  983 =================
  984 CL_ReadPackets
  985 =================
  986 */
  987 void CL_ReadPackets (void)
  988 {
  989         while (NET_GetPacket (NS_CLIENT, &net_from, &net_message))
  990         {
  991 //      Com_Printf ("packet\n");
  992                 //
  993                 // remote command packet
  994                 //
  995                 if (*(int *)net_message.data == -1)
  996                 {
  997                         CL_ConnectionlessPacket ();
  998                         continue;
  999                 }
 1000 
 1001                 if (cls.state == ca_disconnected || cls.state == ca_connecting)
 1002                         continue;               // dump it if not connected
 1003 
 1004                 if (net_message.cursize < 8)
 1005                 {
 1006                         Com_Printf ("%s: Runt packet\n",NET_AdrToString(net_from));
 1007                         continue;
 1008                 }
 1009 
 1010                 //
 1011                 // packet from server
 1012                 //
 1013                 if (!NET_CompareAdr (net_from, cls.netchan.remote_address))
 1014                 {
 1015                         Com_DPrintf ("%s:sequenced packet without connection\n"
 1016                                 ,NET_AdrToString(net_from));
 1017                         continue;
 1018                 }
 1019                 if (!Netchan_Process(&cls.netchan, &net_message))
 1020                         continue;               // wasn't accepted for some reason
 1021                 CL_ParseServerMessage ();
 1022         }
 1023 
 1024         //
 1025         // check timeout
 1026         //
 1027         if (cls.state >= ca_connected
 1028          && cls.realtime - cls.netchan.last_received > cl_timeout->value*1000)
 1029         {
 1030                 if (++cl.timeoutcount > 5)      // timeoutcount saves debugger
 1031                 {
 1032                         Com_Printf ("\nServer connection timed out.\n");
 1033                         CL_Disconnect ();
 1034                         return;
 1035                 }
 1036         }
 1037         else
 1038                 cl.timeoutcount = 0;
 1039         
 1040 }
 1041 
 1042 
 1043 //=============================================================================
 1044 
 1045 /*
 1046 ==============
 1047 CL_FixUpGender_f
 1048 ==============
 1049 */
 1050 void CL_FixUpGender(void)
 1051 {
 1052         char *p;
 1053         char sk[80];
 1054 
 1055         if (gender_auto->value) {
 1056 
 1057                 if (gender->modified) {
 1058                         // was set directly, don't override the user
 1059                         gender->modified = false;
 1060                         return;
 1061                 }
 1062 
 1063                 strncpy(sk, skin->string, sizeof(sk) - 1);
 1064                 if ((p = strchr(sk, '/')) != NULL)
 1065                         *p = 0;
 1066                 if (Q_stricmp(sk, "male") == 0 || Q_stricmp(sk, "cyborg") == 0)
 1067                         Cvar_Set ("gender", "male");
 1068                 else if (Q_stricmp(sk, "female") == 0 || Q_stricmp(sk, "crackhor") == 0)
 1069                         Cvar_Set ("gender", "female");
 1070                 else
 1071                         Cvar_Set ("gender", "none");
 1072                 gender->modified = false;
 1073         }
 1074 }
 1075 
 1076 /*
 1077 ==============
 1078 CL_Userinfo_f
 1079 ==============
 1080 */
 1081 void CL_Userinfo_f (void)
 1082 {
 1083         Com_Printf ("User info settings:\n");
 1084         Info_Print (Cvar_Userinfo());
 1085 }
 1086 
 1087 /*
 1088 =================
 1089 CL_Snd_Restart_f
 1090 
 1091 Restart the sound subsystem so it can pick up
 1092 new parameters and flush all sounds
 1093 =================
 1094 */
 1095 void CL_Snd_Restart_f (void)
 1096 {
 1097         S_Shutdown ();
 1098         S_Init ();
 1099         CL_RegisterSounds ();
 1100 }
 1101 
 1102 int precache_check; // for autodownload of precache items
 1103 int precache_spawncount;
 1104 int precache_tex;
 1105 int precache_model_skin;
 1106 
 1107 byte *precache_model; // used for skin checking in alias models
 1108 
 1109 #define PLAYER_MULT 5
 1110 
 1111 // ENV_CNT is map load, ENV_CNT+1 is first env map
 1112 #define ENV_CNT (CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
 1113 #define TEXTURE_CNT (ENV_CNT+13)
 1114 
 1115 static const char *env_suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
 1116 
 1117 void CL_RequestNextDownload (void)
 1118 {
 1119         unsigned        map_checksum;           // for detecting cheater maps
 1120         char fn[MAX_OSPATH];
 1121         dmdl_t *pheader;
 1122 
 1123         if (cls.state != ca_connected)
 1124                 return;
 1125 
 1126         if (!allow_download->value && precache_check < ENV_CNT)
 1127                 precache_check = ENV_CNT;
 1128 
 1129 //ZOID
 1130         if (precache_check == CS_MODELS) { // confirm map
 1131                 precache_check = CS_MODELS+2; // 0 isn't used
 1132                 if (allow_download_maps->value)
 1133                         if (!CL_CheckOrDownloadFile(cl.configstrings[CS_MODELS+1]))
 1134                                 return; // started a download
 1135         }
 1136         if (precache_check >= CS_MODELS && precache_check < CS_MODELS+MAX_MODELS) {
 1137                 if (allow_download_models->value) {
 1138                         while (precache_check < CS_MODELS+MAX_MODELS &&
 1139                                 cl.configstrings[precache_check][0]) {
 1140                                 if (cl.configstrings[precache_check][0] == '*' ||
 1141                                         cl.configstrings[precache_check][0] == '#') {
 1142                                         precache_check++;
 1143                                         continue;
 1144                                 }
 1145                                 if (precache_model_skin == 0) {
 1146                                         if (!CL_CheckOrDownloadFile(cl.configstrings[precache_check])) {
 1147                                                 precache_model_skin = 1;
 1148                                                 return; // started a download
 1149                                         }
 1150                                         precache_model_skin = 1;
 1151                                 }
 1152 
 1153                                 // checking for skins in the model
 1154                                 if (!precache_model) {
 1155 
 1156                                         FS_LoadFile (cl.configstrings[precache_check], (void **)&precache_model);
 1157                                         if (!precache_model) {
 1158                                                 precache_model_skin = 0;
 1159                                                 precache_check++;
 1160                                                 continue; // couldn't load it
 1161                                         }
 1162                                         if (LittleLong(*(unsigned *)precache_model) != IDALIASHEADER) {
 1163                                                 // not an alias model
 1164                                                 FS_FreeFile(precache_model);
 1165                                                 precache_model = 0;
 1166                                                 precache_model_skin = 0;
 1167                                                 precache_check++;
 1168                                                 continue;
 1169                                         }
 1170                                         pheader = (dmdl_t *)precache_model;
 1171                                         if (LittleLong (pheader->version) != ALIAS_VERSION) {
 1172                                                 precache_check++;
 1173                                                 precache_model_skin = 0;
 1174                                                 continue; // couldn't load it
 1175                                         }
 1176                                 }
 1177 
 1178                                 pheader = (dmdl_t *)precache_model;
 1179 
 1180                                 while (precache_model_skin - 1 < LittleLong(pheader->num_skins)) {
 1181                                         if (!CL_CheckOrDownloadFile((char *)precache_model +
 1182                                                 LittleLong(pheader->ofs_skins) + 
 1183                                                 (precache_model_skin - 1)*MAX_SKINNAME)) {
 1184                                                 precache_model_skin++;
 1185                                                 return; // started a download
 1186                                         }
 1187                                         precache_model_skin++;
 1188                                 }
 1189                                 if (precache_model) { 
 1190                                         FS_FreeFile(precache_model);
 1191                                         precache_model = 0;
 1192                                 }
 1193                                 precache_model_skin = 0;
 1194                                 precache_check++;
 1195                         }
 1196                 }
 1197                 precache_check = CS_SOUNDS;
 1198         }
 1199         if (precache_check >= CS_SOUNDS && precache_check < CS_SOUNDS+MAX_SOUNDS) { 
 1200                 if (allow_download_sounds->value) {
 1201                         if (precache_check == CS_SOUNDS)
 1202                                 precache_check++; // zero is blank
 1203                         while (precache_check < CS_SOUNDS+MAX_SOUNDS &&
 1204                                 cl.configstrings[precache_check][0]) {
 1205                                 if (cl.configstrings[precache_check][0] == '*') {
 1206                                         precache_check++;
 1207                                         continue;
 1208                                 }
 1209                                 Com_sprintf(fn, sizeof(fn), "sound/%s", cl.configstrings[precache_check++]);
 1210                                 if (!CL_CheckOrDownloadFile(fn))
 1211                                         return; // started a download
 1212                         }
 1213                 }
 1214                 precache_check = CS_IMAGES;
 1215         }
 1216         if (precache_check >= CS_IMAGES && precache_check < CS_IMAGES+MAX_IMAGES) {
 1217                 if (precache_check == CS_IMAGES)
 1218                         precache_check++; // zero is blank
 1219                 while (precache_check < CS_IMAGES+MAX_IMAGES &&
 1220                         cl.configstrings[precache_check][0]) {
 1221                         Com_sprintf(fn, sizeof(fn), "pics/%s.pcx", cl.configstrings[precache_check++]);
 1222                         if (!CL_CheckOrDownloadFile(fn))
 1223                                 return; // started a download
 1224                 }
 1225                 precache_check = CS_PLAYERSKINS;
 1226         }
 1227         // skins are special, since a player has three things to download:
 1228         // model, weapon model and skin
 1229         // so precache_check is now *3
 1230         if (precache_check >= CS_PLAYERSKINS && precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) {
 1231                 if (allow_download_players->value) {
 1232                         while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT) {
 1233                                 int i, n;
 1234                                 char model[MAX_QPATH], skin[MAX_QPATH], *p;
 1235 
 1236                                 i = (precache_check - CS_PLAYERSKINS)/PLAYER_MULT;
 1237                                 n = (precache_check - CS_PLAYERSKINS)%PLAYER_MULT;
 1238 
 1239                                 if (!cl.configstrings[CS_PLAYERSKINS+i][0]) {
 1240                                         precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
 1241                                         continue;
 1242                                 }
 1243 
 1244                                 if ((p = strchr(cl.configstrings[CS_PLAYERSKINS+i], '\\')) != NULL)
 1245                                         p++;
 1246                                 else
 1247                                         p = cl.configstrings[CS_PLAYERSKINS+i];
 1248                                 strcpy(model, p);
 1249                                 p = strchr(model, '/');
 1250                                 if (!p)
 1251                                         p = strchr(model, '\\');
 1252                                 if (p) {
 1253                                         *p++ = 0;
 1254                                         strcpy(skin, p);
 1255                                 } else
 1256                                         *skin = 0;
 1257 
 1258                                 switch (n) {
 1259                                 case 0: // model
 1260                                         Com_sprintf(fn, sizeof(fn), "players/%s/tris.md2", model);
 1261                                         if (!CL_CheckOrDownloadFile(fn)) {
 1262                                                 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1;
 1263                                                 return; // started a download
 1264                                         }
 1265                                         n++;
 1266                                         /*FALL THROUGH*/
 1267 
 1268                                 case 1: // weapon model
 1269                                         Com_sprintf(fn, sizeof(fn), "players/%s/weapon.md2", model);
 1270                                         if (!CL_CheckOrDownloadFile(fn)) {
 1271                                                 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2;
 1272                                                 return; // started a download
 1273                                         }
 1274                                         n++;
 1275                                         /*FALL THROUGH*/
 1276 
 1277                                 case 2: // weapon skin
 1278                                         Com_sprintf(fn, sizeof(fn), "players/%s/weapon.pcx", model);
 1279                                         if (!CL_CheckOrDownloadFile(fn)) {
 1280                                                 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3;
 1281                                                 return; // started a download
 1282                                         }
 1283                                         n++;
 1284                                         /*FALL THROUGH*/
 1285 
 1286                                 case 3: // skin
 1287                                         Com_sprintf(fn, sizeof(fn), "players/%s/%s.pcx", model, skin);
 1288                                         if (!CL_CheckOrDownloadFile(fn)) {
 1289                                                 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4;
 1290                                                 return; // started a download
 1291                                         }
 1292                                         n++;
 1293                                         /*FALL THROUGH*/
 1294 
 1295                                 case 4: // skin_i
 1296                                         Com_sprintf(fn, sizeof(fn), "players/%s/%s_i.pcx", model, skin);
 1297                                         if (!CL_CheckOrDownloadFile(fn)) {
 1298                                                 precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5;
 1299                                                 return; // started a download
 1300                                         }
 1301                                         // move on to next model
 1302                                         precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
 1303                                 }
 1304                         }
 1305                 }
 1306                 // precache phase completed
 1307                 precache_check = ENV_CNT;
 1308         }
 1309 
 1310         if (precache_check == ENV_CNT) {
 1311                 precache_check = ENV_CNT + 1;
 1312 
 1313                 CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
 1314 
 1315                 if (map_checksum != atoi(cl.configstrings[CS_MAPCHECKSUM])) {
 1316                         Com_Error (ERR_DROP, "Local map version differs from server: %i != '%s'\n",
 1317                                 map_checksum, cl.configstrings[CS_MAPCHECKSUM]);
 1318                         return;
 1319                 }
 1320         }
 1321 
 1322         if (precache_check > ENV_CNT && precache_check < TEXTURE_CNT) {
 1323                 if (allow_download->value && allow_download_maps->value) {
 1324                         while (precache_check < TEXTURE_CNT) {
 1325                                 int n = precache_check++ - ENV_CNT - 1;
 1326 
 1327                                 if (n & 1)
 1328                                         Com_sprintf(fn, sizeof(fn), "env/%s%s.pcx", 
 1329                                                 cl.configstrings[CS_SKY], env_suf[n/2]);
 1330                                 else
 1331                                         Com_sprintf(fn, sizeof(fn), "env/%s%s.tga", 
 1332                                                 cl.configstrings[CS_SKY], env_suf[n/2]);
 1333                                 if (!CL_CheckOrDownloadFile(fn))
 1334                                         return; // started a download
 1335                         }
 1336                 }
 1337                 precache_check = TEXTURE_CNT;
 1338         }
 1339 
 1340         if (precache_check == TEXTURE_CNT) {
 1341                 precache_check = TEXTURE_CNT+1;
 1342                 precache_tex = 0;
 1343         }
 1344 
 1345         // confirm existance of textures, download any that don't exist
 1346         if (precache_check == TEXTURE_CNT+1) {
 1347                 // from qcommon/cmodel.c
 1348                 extern int                      numtexinfo;
 1349                 extern mapsurface_t     map_surfaces[];
 1350 
 1351                 if (allow_download->value && allow_download_maps->value) {
 1352                         while (precache_tex < numtexinfo) {
 1353                                 char fn[MAX_OSPATH];
 1354 
 1355                                 sprintf(fn, "textures/%s.wal", map_surfaces[precache_tex++].rname);
 1356                                 if (!CL_CheckOrDownloadFile(fn))
 1357                                         return; // started a download
 1358                         }
 1359                 }
 1360                 precache_check = TEXTURE_CNT+999;
 1361         }
 1362 
 1363 //ZOID
 1364         CL_RegisterSounds ();
 1365         CL_PrepRefresh ();
 1366 
 1367         MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
 1368         MSG_WriteString (&cls.netchan.message, va("begin %i\n", precache_spawncount) );
 1369 }
 1370 
 1371 /*
 1372 =================
 1373 CL_Precache_f
 1374 
 1375 The server will send this command right
 1376 before allowing the client into the server
 1377 =================
 1378 */
 1379 void CL_Precache_f (void)
 1380 {
 1381         //Yet another hack to let old demos work
 1382         //the old precache sequence
 1383         if (Cmd_Argc() < 2) {
 1384                 unsigned        map_checksum;           // for detecting cheater maps
 1385 
 1386                 CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
 1387                 CL_RegisterSounds ();
 1388                 CL_PrepRefresh ();
 1389                 return;
 1390         }
 1391 
 1392         precache_check = CS_MODELS;
 1393         precache_spawncount = atoi(Cmd_Argv(1));
 1394         precache_model = 0;
 1395         precache_model_skin = 0;
 1396 
 1397         CL_RequestNextDownload();
 1398 }
 1399 
 1400 
 1401 /*
 1402 =================
 1403 CL_InitLocal
 1404 =================
 1405 */
 1406 void CL_InitLocal (void)
 1407 {
 1408         cls.state = ca_disconnected;
 1409         cls.realtime = Sys_Milliseconds ();
 1410 
 1411         CL_InitInput ();
 1412 
 1413         adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE );
 1414         adr1 = Cvar_Get( "adr1", "", CVAR_ARCHIVE );
 1415         adr2 = Cvar_Get( "adr2", "", CVAR_ARCHIVE );
 1416         adr3 = Cvar_Get( "adr3", "", CVAR_ARCHIVE );
 1417         adr4 = Cvar_Get( "adr4", "", CVAR_ARCHIVE );
 1418         adr5 = Cvar_Get( "adr5", "", CVAR_ARCHIVE );
 1419         adr6 = Cvar_Get( "adr6", "", CVAR_ARCHIVE );
 1420         adr7 = Cvar_Get( "adr7", "", CVAR_ARCHIVE );
 1421         adr8 = Cvar_Get( "adr8", "", CVAR_ARCHIVE );
 1422 
 1423 //
 1424 // register our variables
 1425 //
 1426         cl_stereo_separation = Cvar_Get( "cl_stereo_separation", "0.4", CVAR_ARCHIVE );
 1427         cl_stereo = Cvar_Get( "cl_stereo", "0", 0 );
 1428 
 1429         cl_add_blend = Cvar_Get ("cl_blend", "1", 0);
 1430         cl_add_lights = Cvar_Get ("cl_lights", "1", 0);
 1431         cl_add_particles = Cvar_Get ("cl_particles", "1", 0);
 1432         cl_add_entities = Cvar_Get ("cl_entities", "1", 0);
 1433         cl_gun = Cvar_Get ("cl_gun", "1", 0);
 1434         cl_footsteps = Cvar_Get ("cl_footsteps", "1", 0);
 1435         cl_noskins = Cvar_Get ("cl_noskins", "0", 0);
 1436         cl_autoskins = Cvar_Get ("cl_autoskins", "0", 0);
 1437         cl_predict = Cvar_Get ("cl_predict", "1", 0);
 1438 //      cl_minfps = Cvar_Get ("cl_minfps", "5", 0);
 1439         cl_maxfps = Cvar_Get ("cl_maxfps", "90", 0);
 1440 
 1441         cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0);
 1442         cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0);
 1443         cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0);
 1444         cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", 0);
 1445         cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", 0);
 1446         cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
 1447 
 1448         cl_run = Cvar_Get ("cl_run", "0", CVAR_ARCHIVE);
 1449         freelook = Cvar_Get( "freelook", "0", CVAR_ARCHIVE );
 1450         lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE);
 1451         lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE);
 1452         sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE);
 1453 
 1454         m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
 1455         m_yaw = Cvar_Get ("m_yaw", "0.022", 0);
 1456         m_forward = Cvar_Get ("m_forward", "1", 0);
 1457         m_side = Cvar_Get ("m_side", "1", 0);
 1458 
 1459         cl_shownet = Cvar_Get ("cl_shownet", "0", 0);
 1460         cl_showmiss = Cvar_Get ("cl_showmiss", "0", 0);
 1461         cl_showclamp = Cvar_Get ("showclamp", "0", 0);
 1462         cl_timeout = Cvar_Get ("cl_timeout", "120", 0);
 1463         cl_paused = Cvar_Get ("paused", "0", 0);
 1464         cl_timedemo = Cvar_Get ("timedemo", "0", 0);
 1465 
 1466         rcon_client_password = Cvar_Get ("rcon_password", "", 0);
 1467         rcon_address = Cvar_Get ("rcon_address", "", 0);
 1468 
 1469         cl_lightlevel = Cvar_Get ("r_lightlevel", "0", 0);
 1470 
 1471         //
 1472         // userinfo
 1473         //
 1474         info_password = Cvar_Get ("password", "", CVAR_USERINFO);
 1475         info_spectator = Cvar_Get ("spectator", "0", CVAR_USERINFO);
 1476         name = Cvar_Get ("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE);
 1477         skin = Cvar_Get ("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE);
 1478         rate = Cvar_Get ("rate", "25000", CVAR_USERINFO | CVAR_ARCHIVE);        // FIXME
 1479         msg = Cvar_Get ("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE);
 1480         hand = Cvar_Get ("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
 1481         fov = Cvar_Get ("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE);
 1482         gender = Cvar_Get ("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE);
 1483         gender_auto = Cvar_Get ("gender_auto", "1", CVAR_ARCHIVE);
 1484         gender->modified = false; // clear this so we know when user sets it manually
 1485 
 1486         cl_vwep = Cvar_Get ("cl_vwep", "1", CVAR_ARCHIVE);
 1487 
 1488 
 1489         //
 1490         // register our commands
 1491         //
 1492         Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
 1493         Cmd_AddCommand ("pause", CL_Pause_f);
 1494         Cmd_AddCommand ("pingservers", CL_PingServers_f);
 1495         Cmd_AddCommand ("skins", CL_Skins_f);
 1496 
 1497         Cmd_AddCommand ("userinfo", CL_Userinfo_f);
 1498         Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
 1499 
 1500         Cmd_AddCommand ("changing", CL_Changing_f);
 1501         Cmd_AddCommand ("disconnect", CL_Disconnect_f);
 1502         Cmd_AddCommand ("record", CL_Record_f);
 1503         Cmd_AddCommand ("stop", CL_Stop_f);
 1504 
 1505         Cmd_AddCommand ("quit", CL_Quit_f);
 1506 
 1507         Cmd_AddCommand ("connect", CL_Connect_f);
 1508         Cmd_AddCommand ("reconnect", CL_Reconnect_f);
 1509 
 1510         Cmd_AddCommand ("rcon", CL_Rcon_f);
 1511 
 1512 //      Cmd_AddCommand ("packet", CL_Packet_f); // this is dangerous to leave in
 1513 
 1514         Cmd_AddCommand ("setenv", CL_Setenv_f );
 1515 
 1516         Cmd_AddCommand ("precache", CL_Precache_f);
 1517 
 1518         Cmd_AddCommand ("download", CL_Download_f);
 1519 
 1520         //
 1521         // forward to server commands
 1522         //
 1523         // the only thing this does is allow command completion
 1524         // to work -- all unknown commands are automatically
 1525         // forwarded to the server
 1526         Cmd_AddCommand ("wave", NULL);
 1527         Cmd_AddCommand ("inven", NULL);
 1528         Cmd_AddCommand ("kill", NULL);
 1529         Cmd_AddCommand ("use", NULL);
 1530         Cmd_AddCommand ("drop", NULL);
 1531         Cmd_AddCommand ("say", NULL);
 1532         Cmd_AddCommand ("say_team", NULL);
 1533         Cmd_AddCommand ("info", NULL);
 1534         Cmd_AddCommand ("prog", NULL);
 1535         Cmd_AddCommand ("give", NULL);
 1536         Cmd_AddCommand ("god", NULL);
 1537         Cmd_AddCommand ("notarget", NULL);
 1538         Cmd_AddCommand ("noclip", NULL);
 1539         Cmd_AddCommand ("invuse", NULL);
 1540         Cmd_AddCommand ("invprev", NULL);
 1541         Cmd_AddCommand ("invnext", NULL);
 1542         Cmd_AddCommand ("invdrop", NULL);
 1543         Cmd_AddCommand ("weapnext", NULL);
 1544         Cmd_AddCommand ("weapprev", NULL);
 1545 }
 1546 
 1547 
 1548 
 1549 /*
 1550 ===============
 1551 CL_WriteConfiguration
 1552 
 1553 Writes key bindings and archived cvars to config.cfg
 1554 ===============
 1555 */
 1556 void CL_WriteConfiguration (void)
 1557 {
 1558         FILE    *f;
 1559         char    path[MAX_QPATH];
 1560 
 1561         if (cls.state == ca_uninitialized)
 1562                 return;
 1563 
 1564         Com_sprintf (path, sizeof(path),"%s/config.cfg",FS_Gamedir());
 1565         f = fopen (path, "w");
 1566         if (!f)
 1567         {
 1568                 Com_Printf ("Couldn't write config.cfg.\n");
 1569                 return;
 1570         }
 1571 
 1572         fprintf (f, "// generated by quake, do not modify\n");
 1573         Key_WriteBindings (f);
 1574         fclose (f);
 1575 
 1576         Cvar_WriteVariables (path);
 1577 }
 1578 
 1579 
 1580 /*
 1581 ==================
 1582 CL_FixCvarCheats
 1583 
 1584 ==================
 1585 */
 1586 
 1587 typedef struct
 1588 {
 1589         char    *name;
 1590         char    *value;
 1591         cvar_t  *var;
 1592 } cheatvar_t;
 1593 
 1594 cheatvar_t      cheatvars[] = {
 1595         {"timescale", "1"},
 1596         {"timedemo", "0"},
 1597         {"r_drawworld", "1"},
 1598         {"cl_testlights", "0"},
 1599         {"r_fullbright", "0"},
 1600         {"r_drawflat", "0"},
 1601         {"paused", "0"},
 1602         {"fixedtime", "0"},
 1603         {"sw_draworder", "0"},
 1604         {"gl_lightmap", "0"},
 1605         {"gl_saturatelighting", "0"},
 1606         {NULL, NULL}
 1607 };
 1608 
 1609 int             numcheatvars;
 1610 
 1611 void CL_FixCvarCheats (void)
 1612 {
 1613         int                     i;
 1614         cheatvar_t      *var;
 1615 
 1616         if ( !strcmp(cl.configstrings[CS_MAXCLIENTS], "1") 
 1617                 || !cl.configstrings[CS_MAXCLIENTS][0] )
 1618                 return;         // single player can cheat
 1619 
 1620         // find all the cvars if we haven't done it yet
 1621         if (!numcheatvars)
 1622         {
 1623                 while (cheatvars[numcheatvars].name)
 1624                 {
 1625                         cheatvars[numcheatvars].var = Cvar_Get (cheatvars[numcheatvars].name,
 1626                                         cheatvars[numcheatvars].value, 0);
 1627                         numcheatvars++;
 1628                 }
 1629         }
 1630 
 1631         // make sure they are all set to the proper values
 1632         for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
 1633         {
 1634                 if ( strcmp (var->var->string, var->value) )
 1635                 {
 1636                         Cvar_Set (var->name, var->value);
 1637                 }
 1638         }
 1639 }
 1640 
 1641 //============================================================================
 1642 
 1643 /*
 1644 ==================
 1645 CL_SendCommand
 1646 
 1647 ==================
 1648 */
 1649 void CL_SendCommand (void)
 1650 {
 1651         // get new key events
 1652         Sys_SendKeyEvents ();
 1653 
 1654         // allow mice or other external controllers to add commands
 1655         IN_Commands ();
 1656 
 1657         // process console commands
 1658         Cbuf_Execute ();
 1659 
 1660         // fix any cheating cvars
 1661         CL_FixCvarCheats ();
 1662 
 1663         // send intentions now
 1664         CL_SendCmd ();
 1665 
 1666         // resend a connection request if necessary
 1667         CL_CheckForResend ();
 1668 }
 1669 
 1670 
 1671 /*
 1672 ==================
 1673 CL_Frame
 1674 
 1675 ==================
 1676 */
 1677 void CL_Frame (int msec)
 1678 {
 1679         static int      extratime;
 1680         static int  lasttimecalled;
 1681 
 1682         if (dedicated->value)
 1683                 return;
 1684 
 1685         extratime += msec;
 1686 
 1687         if (!cl_timedemo->value)
 1688         {
 1689                 if (cls.state == ca_connected && extratime < 100)
 1690                         return;                 // don't flood packets out while connecting
 1691                 if (extratime < 1000/cl_maxfps->value)
 1692                         return;                 // framerate is too high
 1693         }
 1694 
 1695         // let the mouse activate or deactivate
 1696         IN_Frame ();
 1697 
 1698         // decide the simulation time
 1699         cls.frametime = extratime/1000.0;
 1700         cl.time += extratime;
 1701         cls.realtime = curtime;
 1702 
 1703         extratime = 0;
 1704 #if 0
1705 if (cls.frametime > (1.0 / cl_minfps->value)) 1706 cls.frametime = (1.0 / cl_minfps->value);
1707 #else 1708 if (cls.frametime > (1.0 / 5)) 1709 cls.frametime = (1.0 / 5); 1710 #endif 1711 1712 // if in the debugger last frame, don't timeout 1713 if (msec > 5000) 1714 cls.netchan.last_received = Sys_Milliseconds (); 1715 1716 // fetch results from server 1717 CL_ReadPackets (); 1718 1719 // send a new command message to the server 1720 CL_SendCommand (); 1721 1722 // predict all unacknowledged movements 1723 CL_PredictMovement (); 1724 1725 // allow rendering DLL change 1726 VID_CheckChanges (); 1727 if (!cl.refresh_prepped && cls.state == ca_active) 1728 CL_PrepRefresh (); 1729 1730 // update the screen 1731 if (host_speeds->value) 1732 time_before_ref = Sys_Milliseconds (); 1733 SCR_UpdateScreen (); 1734 if (host_speeds->value) 1735 time_after_ref = Sys_Milliseconds (); 1736 1737 // update audio 1738 S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up); 1739 1740 CDAudio_Update(); 1741 1742 // advance local effects for next frame 1743 CL_RunDLights (); 1744 CL_RunLightStyles (); 1745 SCR_RunCinematic (); 1746 SCR_RunConsole (); 1747 1748 cls.framecount++; 1749 1750 if ( log_stats->value ) 1751 { 1752 if ( cls.state == ca_active ) 1753 { 1754 if ( !lasttimecalled ) 1755 { 1756 lasttimecalled = Sys_Milliseconds(); 1757 if ( log_stats_file ) 1758 fprintf( log_stats_file, "0\n" ); 1759 } 1760 else 1761 { 1762 int now = Sys_Milliseconds(); 1763 1764 if ( log_stats_file ) 1765 fprintf( log_stats_file, "%d\n", now - lasttimecalled ); 1766 lasttimecalled = now; 1767 } 1768 } 1769 } 1770 } 1771 1772 1773 //============================================================================ 1774 1775 /* 1776 ==================== 1777 CL_Init 1778 ==================== 1779 */ 1780 void CL_Init (void) 1781 { 1782 if (dedicated->value) 1783 return; // nothing running on the client 1784 1785 // all archived variables will now be loaded 1786 1787 Con_Init (); 1788 #if defined __linux__ || defined __sgi
1789 S_Init (); 1790 VID_Init ();
1791 #else 1792 VID_Init (); 1793 S_Init (); // sound must be initialized after window is created 1794 #endif 1795 1796 V_Init (); 1797 1798 net_message.data = net_message_buffer; 1799 net_message.maxsize = sizeof(net_message_buffer); 1800 1801 M_Init (); 1802 1803 SCR_Init (); 1804 cls.disable_screen = true; // don't draw yet 1805 1806 CDAudio_Init (); 1807 CL_InitLocal (); 1808 IN_Init (); 1809 1810 // Cbuf_AddText ("exec autoexec.cfg\n"); 1811 FS_ExecAutoexec (); 1812 Cbuf_Execute (); 1813 1814 } 1815 1816 1817 /* 1818 =============== 1819 CL_Shutdown 1820 1821 FIXME: this is a callback from Sys_Quit and Com_Error. It would be better 1822 to run quit through here before the final handoff to the sys code. 1823 =============== 1824 */ 1825 void CL_Shutdown(void) 1826 { 1827 static qboolean isdown = false; 1828 1829 if (isdown) 1830 { 1831 printf ("recursive shutdown\n"); 1832 return; 1833 } 1834 isdown = true; 1835 1836 CL_WriteConfiguration (); 1837 1838 CDAudio_Shutdown (); 1839 S_Shutdown(); 1840 IN_Shutdown (); 1841 VID_Shutdown(); 1842 } 1843 1844 1845