Quake-2

Quake 2 GPL Source Release
Log | Files | Refs

cl_main.c (40005B)


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