Home | History | Annotate | Download | only in client
      1 /*
      2 Copyright (C) 1996-1997 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 "quakedef.h"
     23 #include "winquake.h"
     24 #ifdef _WIN32
     25 #include "winsock.h"
     26 #else
     27 #include <netinet/in.h>
     28 #endif
     29 
     30 
     31 // we need to declare some mouse variables here, because the menu system
     32 // references them even when on a unix system.
     33 
     34 qboolean	noclip_anglehack;		// remnant from old quake
     35 
     36 
     37 cvar_t	rcon_password = CVAR3("rcon_password", "", false);
     38 
     39 cvar_t	rcon_address = CVAR2("rcon_address", "");
     40 
     41 cvar_t	cl_timeout = CVAR2("cl_timeout", "60");
     42 
     43 cvar_t	cl_shownet = CVAR2("cl_shownet","0");	// can be 0, 1, or 2
     44 
     45 cvar_t	cl_sbar		= CVAR3("cl_sbar", "0", true);
     46 cvar_t	cl_hudswap	= CVAR3("cl_hudswap", "0", true);
     47 cvar_t	cl_maxfps	= CVAR3("cl_maxfps", "0", true);
     48 
     49 cvar_t	lookspring = CVAR3("lookspring","0", true);
     50 cvar_t	lookstrafe = CVAR3("lookstrafe","0", true);
     51 cvar_t	sensitivity = CVAR3("sensitivity","3", true);
     52 
     53 cvar_t	m_pitch = CVAR3("m_pitch","0.022", true);
     54 cvar_t	m_yaw = CVAR2("m_yaw","0.022");
     55 cvar_t	m_forward = CVAR2("m_forward","1");
     56 cvar_t	m_side = CVAR2("m_side","0.8");
     57 
     58 cvar_t	entlatency = CVAR2("entlatency", "20");
     59 cvar_t	cl_predict_players = CVAR2("cl_predict_players", "1");
     60 cvar_t	cl_predict_players2 = CVAR2("cl_predict_players2", "1");
     61 cvar_t	cl_solid_players = CVAR2("cl_solid_players", "1");
     62 
     63 cvar_t  localid = CVAR2("localid", "");
     64 
     65 static qboolean allowremotecmd = true;
     66 
     67 //
     68 // info mirrors
     69 //
     70 cvar_t	password = CVAR4("password", "", false, true);
     71 cvar_t	spectator = CVAR4("spectator", "", false, true);
     72 cvar_t	name = CVAR4("name","unnamed", true, true);
     73 cvar_t	team = CVAR4("team","", true, true);
     74 cvar_t	skin = CVAR4("skin","", true, true);
     75 cvar_t	topcolor = CVAR4("topcolor","0", true, true);
     76 cvar_t	bottomcolor = CVAR4("bottomcolor","0", true, true);
     77 cvar_t	rate = CVAR4("rate","2500", true, true);
     78 cvar_t	noaim = CVAR4("noaim","0", true, true);
     79 cvar_t	msg = CVAR4("msg","1", true, true);
     80 
     81 extern cvar_t cl_hightrack;
     82 
     83 
     84 client_static_t	cls;
     85 client_state_t	cl;
     86 
     87 entity_state_t	cl_baselines[MAX_EDICTS];
     88 efrag_t			cl_efrags[MAX_EFRAGS];
     89 entity_t		cl_static_entities[MAX_STATIC_ENTITIES];
     90 lightstyle_t	cl_lightstyle[MAX_LIGHTSTYLES];
     91 dlight_t		cl_dlights[MAX_DLIGHTS];
     92 
     93 // refresh list
     94 // this is double buffered so the last frame
     95 // can be scanned for oldorigins of trailing objects
     96 int				cl_numvisedicts, cl_oldnumvisedicts;
     97 entity_t		*cl_visedicts, *cl_oldvisedicts;
     98 entity_t		cl_visedicts_list[2][MAX_VISEDICTS];
     99 
    100 double			connect_time = -1;		// for connection retransmits
    101 
    102 quakeparms_t host_parms;
    103 
    104 qboolean	host_initialized;		// true if into command execution
    105 qboolean	nomaster;
    106 
    107 double		host_frametime;
    108 double		realtime;				// without any filtering or bounding
    109 double		oldrealtime;			// last frame run
    110 int			host_framecount;
    111 
    112 int			host_hunklevel;
    113 
    114 byte		*host_basepal;
    115 byte		*host_colormap;
    116 
    117 netadr_t	master_adr;				// address of the master server
    118 
    119 cvar_t	host_speeds = CVAR2("host_speeds","0");			// set for running times
    120 cvar_t	show_fps = CVAR2("show_fps","0");			// set for running times
    121 cvar_t	developer = CVAR2("developer","0");
    122 
    123 int			fps_count;
    124 
    125 jmp_buf 	host_abort;
    126 
    127 void Master_Connect_f (void);
    128 
    129 float	server_version = 0;	// version of server we connected to
    130 
    131 char emodel_name[] =
    132 	{ 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
    133 char pmodel_name[] =
    134 	{ 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
    135 char prespawn_name[] =
    136 	{ 'p'^0xff, 'r'^0xff, 'e'^0xff, 's'^0xff, 'p'^0xff, 'a'^0xff, 'w'^0xff, 'n'^0xff,
    137 		' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '0'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
    138 char modellist_name[] =
    139 	{ 'm'^0xff, 'o'^0xff, 'd'^0xff, 'e'^0xff, 'l'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
    140 		' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
    141 char soundlist_name[] =
    142 	{ 's'^0xff, 'o'^0xff, 'u'^0xff, 'n'^0xff, 'd'^0xff, 'l'^0xff, 'i'^0xff, 's'^0xff, 't'^0xff,
    143 		' '^0xff, '%'^0xff, 'i'^0xff, ' '^0xff, '%'^0xff, 'i'^0xff, 0 };
    144 
    145 /*
    146 ==================
    147 CL_Quit_f
    148 ==================
    149 */
    150 void CL_Quit_f (void)
    151 {
    152 	if (1 /* key_dest != key_console */ /* && cls.state != ca_dedicated */)
    153 	{
    154 		M_Menu_Quit_f ();
    155 		return;
    156 	}
    157 	CL_Disconnect ();
    158 	Sys_Quit ();
    159 }
    160 
    161 /*
    162 =======================
    163 CL_Version_f
    164 ======================
    165 */
    166 void CL_Version_f (void)
    167 {
    168 	Con_Printf ("Version %4.2f\n", VERSION);
    169 	Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
    170 }
    171 
    172 
    173 /*
    174 =======================
    175 CL_SendConnectPacket
    176 
    177 called by CL_Connect_f and CL_CheckResend
    178 ======================
    179 */
    180 void CL_SendConnectPacket (void)
    181 {
    182 	netadr_t	adr;
    183 	char	data[2048];
    184 	double t1, t2;
    185 // JACK: Fixed bug where DNS lookups would cause two connects real fast
    186 //       Now, adds lookup time to the connect time.
    187 //		 Should I add it to realtime instead?!?!
    188 
    189 	if (cls.state != ca_disconnected)
    190 		return;
    191 
    192 	t1 = Sys_DoubleTime ();
    193 
    194 	if (!NET_StringToAdr (cls.servername, &adr))
    195 	{
    196 		Con_Printf ("Bad server address\n");
    197 		connect_time = -1;
    198 		return;
    199 	}
    200 
    201 	if (!NET_IsClientLegal(&adr))
    202 	{
    203 		Con_Printf ("Illegal server address\n");
    204 		connect_time = -1;
    205 		return;
    206 	}
    207 
    208 	if (adr.port == 0)
    209 		adr.port = BigShort (27500);
    210 	t2 = Sys_DoubleTime ();
    211 
    212 	connect_time = realtime+t2-t1;	// for retransmit requests
    213 
    214 	cls.qport = Cvar_VariableValue("qport");
    215 
    216 	Info_SetValueForStarKey (cls.userinfo, "*ip", NET_AdrToString(adr), MAX_INFO_STRING);
    217 
    218 //	Con_Printf ("Connecting to %s...\n", cls.servername);
    219 	sprintf (data, "%c%c%c%cconnect %i %i %i \"%s\"\n",
    220 		255, 255, 255, 255,	PROTOCOL_VERSION, cls.qport, cls.challenge, cls.userinfo);
    221 	NET_SendPacket (strlen(data), data, adr);
    222 }
    223 
    224 /*
    225 =================
    226 CL_CheckForResend
    227 
    228 Resend a connect message if the last one has timed out
    229 
    230 =================
    231 */
    232 void CL_CheckForResend (void)
    233 {
    234 	netadr_t	adr;
    235 	char	data[2048];
    236 	double t1, t2;
    237 
    238 	if (connect_time == -1)
    239 		return;
    240 	if (cls.state != ca_disconnected)
    241 		return;
    242 	if (connect_time && realtime - connect_time < 5.0)
    243 		return;
    244 
    245 	t1 = Sys_DoubleTime ();
    246 	if (!NET_StringToAdr (cls.servername, &adr))
    247 	{
    248 		Con_Printf ("Bad server address\n");
    249 		connect_time = -1;
    250 		return;
    251 	}
    252 	if (!NET_IsClientLegal(&adr))
    253 	{
    254 		Con_Printf ("Illegal server address\n");
    255 		connect_time = -1;
    256 		return;
    257 	}
    258 
    259 	if (adr.port == 0)
    260 		adr.port = BigShort (27500);
    261 	t2 = Sys_DoubleTime ();
    262 
    263 	connect_time = realtime+t2-t1;	// for retransmit requests
    264 
    265 	Con_Printf ("Connecting to %s...\n", cls.servername);
    266 	sprintf (data, "%c%c%c%cgetchallenge\n", 255, 255, 255, 255);
    267 	NET_SendPacket (strlen(data), data, adr);
    268 }
    269 
    270 void CL_BeginServerConnect(void)
    271 {
    272 	connect_time = 0;
    273 	CL_CheckForResend();
    274 }
    275 
    276 /*
    277 ================
    278 CL_Connect_f
    279 
    280 ================
    281 */
    282 void CL_Connect_f (void)
    283 {
    284 	char	*server;
    285 
    286 	if (Cmd_Argc() != 2)
    287 	{
    288 		Con_Printf ("usage: connect <server>\n");
    289 		return;
    290 	}
    291 
    292 	server = Cmd_Argv (1);
    293 
    294 	CL_Disconnect ();
    295 
    296 	strncpy (cls.servername, server, sizeof(cls.servername)-1);
    297 	CL_BeginServerConnect();
    298 }
    299 
    300 
    301 /*
    302 =====================
    303 CL_Rcon_f
    304 
    305   Send the rest of the command line over as
    306   an unconnected command.
    307 =====================
    308 */
    309 void CL_Rcon_f (void)
    310 {
    311 	char	message[1024];
    312 	int		i;
    313 	netadr_t	to;
    314 
    315 	if (!rcon_password.string)
    316 	{
    317 		Con_Printf ("You must set 'rcon_password' before\n"
    318 					"issuing an rcon command.\n");
    319 		return;
    320 	}
    321 
    322 	message[0] = 255;
    323 	message[1] = 255;
    324 	message[2] = 255;
    325 	message[3] = 255;
    326 	message[4] = 0;
    327 
    328 	strcat (message, "rcon ");
    329 
    330 	strcat (message, rcon_password.string);
    331 	strcat (message, " ");
    332 
    333 	for (i=1 ; i<Cmd_Argc() ; i++)
    334 	{
    335 		strcat (message, Cmd_Argv(i));
    336 		strcat (message, " ");
    337 	}
    338 
    339 	if (cls.state >= ca_connected)
    340 		to = cls.netchan.remote_address;
    341 	else
    342 	{
    343 		if (!strlen(rcon_address.string))
    344 		{
    345 			Con_Printf ("You must either be connected,\n"
    346 						"or set the 'rcon_address' cvar\n"
    347 						"to issue rcon commands\n");
    348 
    349 			return;
    350 		}
    351 		NET_StringToAdr (rcon_address.string, &to);
    352 	}
    353 
    354 	NET_SendPacket (strlen(message)+1, message
    355 		, to);
    356 }
    357 
    358 
    359 /*
    360 =====================
    361 CL_ClearState
    362 
    363 =====================
    364 */
    365 void CL_ClearState (void)
    366 {
    367 	int			i;
    368 
    369 	S_StopAllSounds (true);
    370 
    371 	Con_DPrintf ("Clearing memory\n");
    372 	D_FlushCaches ();
    373 	Mod_ClearAll ();
    374 	if (host_hunklevel)	// FIXME: check this...
    375 		Hunk_FreeToLowMark (host_hunklevel);
    376 
    377 	CL_ClearTEnts ();
    378 
    379 // wipe the entire cl structure
    380 	memset (&cl, 0, sizeof(cl));
    381 
    382 	SZ_Clear (&cls.netchan.message);
    383 
    384 // clear other arrays
    385 	memset (cl_efrags, 0, sizeof(cl_efrags));
    386 	memset (cl_dlights, 0, sizeof(cl_dlights));
    387 	memset (cl_lightstyle, 0, sizeof(cl_lightstyle));
    388 
    389 //
    390 // allocate the efrags and chain together into a free list
    391 //
    392 	cl.free_efrags = cl_efrags;
    393 	for (i=0 ; i<MAX_EFRAGS-1 ; i++)
    394 		cl.free_efrags[i].entnext = &cl.free_efrags[i+1];
    395 	cl.free_efrags[i].entnext = NULL;
    396 }
    397 
    398 /*
    399 =====================
    400 CL_Disconnect
    401 
    402 Sends a disconnect message to the server
    403 This is also called on Host_Error, so it shouldn't cause any errors
    404 =====================
    405 */
    406 void CL_Disconnect (void)
    407 {
    408 	char	final[10];
    409 
    410 	connect_time = -1;
    411 
    412 #ifdef _WIN32
    413 	SetWindowText (mainwindow, "QuakeWorld: disconnected");
    414 #endif
    415 
    416 // stop sounds (especially looping!)
    417 	S_StopAllSounds (true);
    418 
    419 // if running a local server, shut it down
    420 	if (cls.demoplayback)
    421 		CL_StopPlayback ();
    422 	else if (cls.state != ca_disconnected)
    423 	{
    424 		if (cls.demorecording)
    425 			CL_Stop_f ();
    426 
    427 		final[0] = clc_stringcmd;
    428 		strcpy (final+1, "drop");
    429 		Netchan_Transmit (&cls.netchan, 6, (byte*) final);
    430 		Netchan_Transmit (&cls.netchan, 6, (byte*) final);
    431 		Netchan_Transmit (&cls.netchan, 6, (byte*) final);
    432 
    433 		cls.state = ca_disconnected;
    434 
    435 		cls.demoplayback = cls.demorecording = cls.timedemo = false;
    436 	}
    437 	Cam_Reset();
    438 
    439 	if (cls.download) {
    440 		fclose(cls.download);
    441 		cls.download = NULL;
    442 	}
    443 
    444 	CL_StopUpload();
    445 
    446 }
    447 
    448 void CL_Disconnect_f (void)
    449 {
    450 	CL_Disconnect ();
    451 }
    452 
    453 /*
    454 ====================
    455 CL_User_f
    456 
    457 user <name or userid>
    458 
    459 Dump userdata / masterdata for a user
    460 ====================
    461 */
    462 void CL_User_f (void)
    463 {
    464 	int		uid;
    465 	int		i;
    466 
    467 	if (Cmd_Argc() != 2)
    468 	{
    469 		Con_Printf ("Usage: user <username / userid>\n");
    470 		return;
    471 	}
    472 
    473 	uid = atoi(Cmd_Argv(1));
    474 
    475 	for (i=0 ; i<MAX_CLIENTS ; i++)
    476 	{
    477 		if (!cl.players[i].name[0])
    478 			continue;
    479 		if (cl.players[i].userid == uid
    480 		|| !strcmp(cl.players[i].name, Cmd_Argv(1)) )
    481 		{
    482 			Info_Print (cl.players[i].userinfo);
    483 			return;
    484 		}
    485 	}
    486 	Con_Printf ("User not in server.\n");
    487 }
    488 
    489 /*
    490 ====================
    491 CL_Users_f
    492 
    493 Dump userids for all current players
    494 ====================
    495 */
    496 void CL_Users_f (void)
    497 {
    498 	int		i;
    499 	int		c;
    500 
    501 	c = 0;
    502 	Con_Printf ("userid frags name\n");
    503 	Con_Printf ("------ ----- ----\n");
    504 	for (i=0 ; i<MAX_CLIENTS ; i++)
    505 	{
    506 		if (cl.players[i].name[0])
    507 		{
    508 			Con_Printf ("%6i %4i %s\n", cl.players[i].userid, cl.players[i].frags, cl.players[i].name);
    509 			c++;
    510 		}
    511 	}
    512 
    513 	Con_Printf ("%i total users\n", c);
    514 }
    515 
    516 void CL_Color_f (void)
    517 {
    518 	// just for quake compatability...
    519 	int		top, bottom;
    520 	char	num[16];
    521 
    522 	if (Cmd_Argc() == 1)
    523 	{
    524 		Con_Printf ("\"color\" is \"%s %s\"\n",
    525 			Info_ValueForKey (cls.userinfo, "topcolor"),
    526 			Info_ValueForKey (cls.userinfo, "bottomcolor") );
    527 		Con_Printf ("color <0-13> [0-13]\n");
    528 		return;
    529 	}
    530 
    531 	if (Cmd_Argc() == 2)
    532 		top = bottom = atoi(Cmd_Argv(1));
    533 	else
    534 	{
    535 		top = atoi(Cmd_Argv(1));
    536 		bottom = atoi(Cmd_Argv(2));
    537 	}
    538 
    539 	top &= 15;
    540 	if (top > 13)
    541 		top = 13;
    542 	bottom &= 15;
    543 	if (bottom > 13)
    544 		bottom = 13;
    545 
    546 	sprintf (num, "%i", top);
    547 	Cvar_Set ("topcolor", num);
    548 	sprintf (num, "%i", bottom);
    549 	Cvar_Set ("bottomcolor", num);
    550 }
    551 
    552 /*
    553 ==================
    554 CL_FullServerinfo_f
    555 
    556 Sent by server when serverinfo changes
    557 ==================
    558 */
    559 void CL_FullServerinfo_f (void)
    560 {
    561 	char *p;
    562 	float v;
    563 
    564 	if (Cmd_Argc() != 2)
    565 	{
    566 		Con_Printf ("usage: fullserverinfo <complete info string>\n");
    567 		return;
    568 	}
    569 
    570 	strcpy (cl.serverinfo, Cmd_Argv(1));
    571 
    572 	if ((p = Info_ValueForKey(cl.serverinfo, "*vesion")) && *p) {
    573 		v = Q_atof(p);
    574 		if (v) {
    575 			if (!server_version)
    576 				Con_Printf("Version %1.2f Server\n", v);
    577 			server_version = v;
    578 		}
    579 	}
    580 }
    581 
    582 /*
    583 ==================
    584 CL_FullInfo_f
    585 
    586 Allow clients to change userinfo
    587 ==================
    588 Casey was here :)
    589 */
    590 void CL_FullInfo_f (void)
    591 {
    592 	char	key[512];
    593 	char	value[512];
    594 	char	*o;
    595 	char	*s;
    596 
    597 	if (Cmd_Argc() != 2)
    598 	{
    599 		Con_Printf ("fullinfo <complete info string>\n");
    600 		return;
    601 	}
    602 
    603 	s = Cmd_Argv(1);
    604 	if (*s == '\\')
    605 		s++;
    606 	while (*s)
    607 	{
    608 		o = key;
    609 		while (*s && *s != '\\')
    610 			*o++ = *s++;
    611 		*o = 0;
    612 
    613 		if (!*s)
    614 		{
    615 			Con_Printf ("MISSING VALUE\n");
    616 			return;
    617 		}
    618 
    619 		o = value;
    620 		s++;
    621 		while (*s && *s != '\\')
    622 			*o++ = *s++;
    623 		*o = 0;
    624 
    625 		if (*s)
    626 			s++;
    627 
    628 		if (!strcasecmp(key, pmodel_name) || !strcasecmp(key, emodel_name))
    629 			continue;
    630 
    631 		Info_SetValueForKey (cls.userinfo, key, value, MAX_INFO_STRING);
    632 	}
    633 }
    634 
    635 /*
    636 ==================
    637 CL_SetInfo_f
    638 
    639 Allow clients to change userinfo
    640 ==================
    641 */
    642 void CL_SetInfo_f (void)
    643 {
    644 	if (Cmd_Argc() == 1)
    645 	{
    646 		Info_Print (cls.userinfo);
    647 		return;
    648 	}
    649 	if (Cmd_Argc() != 3)
    650 	{
    651 		Con_Printf ("usage: setinfo [ <key> <value> ]\n");
    652 		return;
    653 	}
    654 	if (!stricmp(Cmd_Argv(1), pmodel_name) || !strcmp(Cmd_Argv(1), emodel_name))
    655 		return;
    656 
    657 	Info_SetValueForKey (cls.userinfo, Cmd_Argv(1), Cmd_Argv(2), MAX_INFO_STRING);
    658 	if (cls.state >= ca_connected)
    659 		Cmd_ForwardToServer ();
    660 }
    661 
    662 /*
    663 ====================
    664 CL_Packet_f
    665 
    666 packet <destination> <contents>
    667 
    668 Contents allows \n escape character
    669 ====================
    670 */
    671 void CL_Packet_f (void)
    672 {
    673 	char	send[2048];
    674 	int		i, l;
    675 	char	*in, *out;
    676 	netadr_t	adr;
    677 
    678 	if (Cmd_Argc() != 3)
    679 	{
    680 		Con_Printf ("packet <destination> <contents>\n");
    681 		return;
    682 	}
    683 
    684 	if (!NET_StringToAdr (Cmd_Argv(1), &adr))
    685 	{
    686 		Con_Printf ("Bad address\n");
    687 		return;
    688 	}
    689 
    690 	in = Cmd_Argv(2);
    691 	out = send+4;
    692 	send[0] = send[1] = send[2] = send[3] = 0xff;
    693 
    694 	l = strlen (in);
    695 	for (i=0 ; i<l ; i++)
    696 	{
    697 		if (in[i] == '\\' && in[i+1] == 'n')
    698 		{
    699 			*out++ = '\n';
    700 			i++;
    701 		}
    702 		else
    703 			*out++ = in[i];
    704 	}
    705 	*out = 0;
    706 
    707 	NET_SendPacket (out-send, send, adr);
    708 }
    709 
    710 
    711 /*
    712 =====================
    713 CL_NextDemo
    714 
    715 Called to play the next demo in the demo loop
    716 =====================
    717 */
    718 void CL_NextDemo (void)
    719 {
    720 	char	str[1024];
    721 
    722 	if (cls.demonum == -1)
    723 		return;		// don't play demos
    724 
    725 	if (!cls.demos[cls.demonum][0] || cls.demonum == MAX_DEMOS)
    726 	{
    727 		cls.demonum = 0;
    728 		if (!cls.demos[cls.demonum][0])
    729 		{
    730 //			Con_Printf ("No demos listed with startdemos\n");
    731 			cls.demonum = -1;
    732 			return;
    733 		}
    734 	}
    735 
    736 	sprintf (str,"playdemo %s\n", cls.demos[cls.demonum]);
    737 	Cbuf_InsertText (str);
    738 	cls.demonum++;
    739 }
    740 
    741 
    742 /*
    743 =================
    744 CL_Changing_f
    745 
    746 Just sent as a hint to the client that they should
    747 drop to full console
    748 =================
    749 */
    750 void CL_Changing_f (void)
    751 {
    752 	if (cls.download)  // don't change when downloading
    753 		return;
    754 
    755 	S_StopAllSounds (true);
    756 	cl.intermission = 0;
    757 	cls.state = ca_connected;	// not active anymore, but not disconnected
    758 	Con_Printf ("\nChanging map...\n");
    759 }
    760 
    761 
    762 /*
    763 =================
    764 CL_Reconnect_f
    765 
    766 The server is changing levels
    767 =================
    768 */
    769 void CL_Reconnect_f (void)
    770 {
    771 	if (cls.download)  // don't change when downloading
    772 		return;
    773 
    774 	S_StopAllSounds (true);
    775 
    776 	if (cls.state == ca_connected) {
    777 		Con_Printf ("reconnecting...\n");
    778 		MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
    779 		MSG_WriteString (&cls.netchan.message, "new");
    780 		return;
    781 	}
    782 
    783 	if (!*cls.servername) {
    784 		Con_Printf("No server to reconnect to...\n");
    785 		return;
    786 	}
    787 
    788 	CL_Disconnect();
    789 	CL_BeginServerConnect();
    790 }
    791 
    792 /*
    793 =================
    794 CL_ConnectionlessPacket
    795 
    796 Responses to broadcasts, etc
    797 =================
    798 */
    799 void CL_ConnectionlessPacket (void)
    800 {
    801 	char	*s;
    802 	int		c;
    803 
    804     MSG_BeginReading ();
    805     MSG_ReadLong ();        // skip the -1
    806 
    807 	c = MSG_ReadByte ();
    808 	if (!cls.demoplayback)
    809 		Con_Printf ("%s: ", NET_AdrToString (net_from));
    810 //	Con_DPrintf ("%s", net_message.data + 5);
    811 	if (c == S2C_CONNECTION)
    812 	{
    813 		Con_Printf ("connection\n");
    814 		if (cls.state >= ca_connected)
    815 		{
    816 			if (!cls.demoplayback)
    817 				Con_Printf ("Dup connect received.  Ignored.\n");
    818 			return;
    819 		}
    820 		Netchan_Setup (&cls.netchan, net_from, cls.qport);
    821 		MSG_WriteChar (&cls.netchan.message, clc_stringcmd);
    822 		MSG_WriteString (&cls.netchan.message, "new");
    823 		cls.state = ca_connected;
    824 		Con_Printf ("Connected.\n");
    825 		allowremotecmd = false; // localid required now for remote cmds
    826 		return;
    827 	}
    828 	// remote command from gui front end
    829 	if (c == A2C_CLIENT_COMMAND)
    830 	{
    831 		char	cmdtext[2048];
    832 
    833 		Con_Printf ("client command\n");
    834 
    835 		if ((*(unsigned *)net_from.ip != *(unsigned *)net_local_adr.ip
    836 			&& *(unsigned *)net_from.ip != htonl(INADDR_LOOPBACK)) )
    837 		{
    838 			Con_Printf ("Command packet from remote host.  Ignored.\n");
    839 			return;
    840 		}
    841 #ifdef _WIN32
    842 		ShowWindow (mainwindow, SW_RESTORE);
    843 		SetForegroundWindow (mainwindow);
    844 #endif
    845 		s = MSG_ReadString ();
    846 
    847 		strncpy(cmdtext, s, sizeof(cmdtext) - 1);
    848 		cmdtext[sizeof(cmdtext) - 1] = 0;
    849 
    850 		s = MSG_ReadString ();
    851 
    852 		while (*s && isspace(*s))
    853 			s++;
    854 		while (*s && isspace(s[strlen(s) - 1]))
    855 			s[strlen(s) - 1] = 0;
    856 
    857 		if (!allowremotecmd && (!*localid.string || strcmp(localid.string, s))) {
    858 			if (!*localid.string) {
    859 				Con_Printf("===========================\n");
    860 				Con_Printf("Command packet received from local host, but no "
    861 					"localid has been set.  You may need to upgrade your server "
    862 					"browser.\n");
    863 				Con_Printf("===========================\n");
    864 				return;
    865 			}
    866 			Con_Printf("===========================\n");
    867 			Con_Printf("Invalid localid on command packet received from local host. "
    868 				"\n|%s| != |%s|\n"
    869 				"You may need to reload your server browser and QuakeWorld.\n",
    870 				s, localid.string);
    871 			Con_Printf("===========================\n");
    872 			Cvar_Set("localid", "");
    873 			return;
    874 		}
    875 
    876 		Cbuf_AddText (cmdtext);
    877 		allowremotecmd = false;
    878 		return;
    879 	}
    880 	// print command from somewhere
    881 	if (c == A2C_PRINT)
    882 	{
    883 		Con_Printf ("print\n");
    884 
    885 		s = MSG_ReadString ();
    886 		Con_Print (s);
    887 		return;
    888 	}
    889 
    890 	// ping from somewhere
    891 	if (c == A2A_PING)
    892 	{
    893 		char	data[6];
    894 
    895 		Con_Printf ("ping\n");
    896 
    897 		data[0] = 0xff;
    898 		data[1] = 0xff;
    899 		data[2] = 0xff;
    900 		data[3] = 0xff;
    901 		data[4] = A2A_ACK;
    902 		data[5] = 0;
    903 
    904 		NET_SendPacket (6, &data, net_from);
    905 		return;
    906 	}
    907 
    908 	if (c == S2C_CHALLENGE) {
    909 		Con_Printf ("challenge\n");
    910 
    911 		s = MSG_ReadString ();
    912 		cls.challenge = atoi(s);
    913 		CL_SendConnectPacket ();
    914 		return;
    915 	}
    916 
    917 #if 0
    918 	if (c == svc_disconnect) {
    919 		Con_Printf ("disconnect\n");
    920 
    921 		Host_EndGame ("Server disconnected");
    922 		return;
    923 	}
    924 #endif
    925 
    926 	Con_Printf ("unknown:  %c\n", c);
    927 }
    928 
    929 
    930 /*
    931 =================
    932 CL_ReadPackets
    933 =================
    934 */
    935 void CL_ReadPackets (void)
    936 {
    937 //	while (NET_GetPacket ())
    938 	while (CL_GetMessage())
    939 	{
    940 		//
    941 		// remote command packet
    942 		//
    943 		if (*(int *)net_message.data == -1)
    944 		{
    945 			CL_ConnectionlessPacket ();
    946 			continue;
    947 		}
    948 
    949 		if (net_message.cursize < 8)
    950 		{
    951 			Con_Printf ("%s: Runt packet\n",NET_AdrToString(net_from));
    952 			continue;
    953 		}
    954 
    955 		//
    956 		// packet from server
    957 		//
    958 		if (!cls.demoplayback &&
    959 			!NET_CompareAdr (net_from, cls.netchan.remote_address))
    960 		{
    961 			Con_DPrintf ("%s:sequenced packet without connection\n"
    962 				,NET_AdrToString(net_from));
    963 			continue;
    964 		}
    965 		if (!Netchan_Process(&cls.netchan))
    966 			continue;		// wasn't accepted for some reason
    967 		CL_ParseServerMessage ();
    968 
    969 //		if (cls.demoplayback && cls.state >= ca_active && !CL_DemoBehind())
    970 //			return;
    971 	}
    972 
    973 	//
    974 	// check timeout
    975 	//
    976 	if (cls.state >= ca_connected
    977 	 && realtime - cls.netchan.last_received > cl_timeout.value)
    978 	{
    979 		Con_Printf ("\nServer connection timed out.\n");
    980 		CL_Disconnect ();
    981 		return;
    982 	}
    983 
    984 }
    985 
    986 //=============================================================================
    987 
    988 /*
    989 =====================
    990 CL_Download_f
    991 =====================
    992 */
    993 void CL_Download_f (void)
    994 {
    995 	char *p, *q;
    996 
    997 	if (cls.state == ca_disconnected)
    998 	{
    999 		Con_Printf ("Must be connected.\n");
   1000 		return;
   1001 	}
   1002 
   1003 	if (Cmd_Argc() != 2)
   1004 	{
   1005 		Con_Printf ("Usage: download <datafile>\n");
   1006 		return;
   1007 	}
   1008 
   1009 	sprintf (cls.downloadname, "%s/%s", com_gamedir, Cmd_Argv(1));
   1010 
   1011 	p = cls.downloadname;
   1012 	for (;;) {
   1013 		if ((q = strchr(p, '/')) != NULL) {
   1014 			*q = 0;
   1015 			Sys_mkdir(cls.downloadname);
   1016 			*q = '/';
   1017 			p = q + 1;
   1018 		} else
   1019 			break;
   1020 	}
   1021 
   1022 	strcpy(cls.downloadtempname, cls.downloadname);
   1023 	cls.download = fopen (cls.downloadname, "wb");
   1024 	cls.downloadtype = dl_single;
   1025 
   1026 	MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
   1027 	SZ_Print (&cls.netchan.message, va("download %s\n",Cmd_Argv(1)));
   1028 }
   1029 
   1030 #ifdef _WINDOWS
   1031 #include <windows.h>
   1032 /*
   1033 =================
   1034 CL_Minimize_f
   1035 =================
   1036 */
   1037 void CL_Windows_f (void) {
   1038 //	if (modestate == MS_WINDOWED)
   1039 //		ShowWindow(mainwindow, SW_MINIMIZE);
   1040 //	else
   1041 		SendMessage(mainwindow, WM_SYSKEYUP, VK_TAB, 1 | (0x0F << 16) | (1<<29));
   1042 }
   1043 #endif
   1044 
   1045 /*
   1046 =================
   1047 CL_Init
   1048 =================
   1049 */
   1050 void CL_Init (void)
   1051 {
   1052 	extern	cvar_t		baseskin;
   1053 	extern	cvar_t		noskins;
   1054 	char st[80];
   1055 
   1056 	cls.state = ca_disconnected;
   1057 
   1058 	Info_SetValueForKey (cls.userinfo, "name", "unnamed", MAX_INFO_STRING);
   1059 	Info_SetValueForKey (cls.userinfo, "topcolor", "0", MAX_INFO_STRING);
   1060 	Info_SetValueForKey (cls.userinfo, "bottomcolor", "0", MAX_INFO_STRING);
   1061 	Info_SetValueForKey (cls.userinfo, "rate", "2500", MAX_INFO_STRING);
   1062 	Info_SetValueForKey (cls.userinfo, "msg", "1", MAX_INFO_STRING);
   1063 	sprintf (st, "%4.2f-%04d", VERSION, build_number());
   1064 	Info_SetValueForStarKey (cls.userinfo, "*ver", st, MAX_INFO_STRING);
   1065 
   1066 	CL_InitInput ();
   1067 	CL_InitTEnts ();
   1068 	CL_InitPrediction ();
   1069 	CL_InitCam ();
   1070 	Pmove_Init ();
   1071 
   1072 //
   1073 // register our commands
   1074 //
   1075 	Cvar_RegisterVariable (&show_fps);
   1076 	Cvar_RegisterVariable (&host_speeds);
   1077 	Cvar_RegisterVariable (&developer);
   1078 
   1079 	Cvar_RegisterVariable (&cl_warncmd);
   1080 	Cvar_RegisterVariable (&cl_upspeed);
   1081 	Cvar_RegisterVariable (&cl_forwardspeed);
   1082 	Cvar_RegisterVariable (&cl_backspeed);
   1083 	Cvar_RegisterVariable (&cl_sidespeed);
   1084 	Cvar_RegisterVariable (&cl_movespeedkey);
   1085 	Cvar_RegisterVariable (&cl_yawspeed);
   1086 	Cvar_RegisterVariable (&cl_pitchspeed);
   1087 	Cvar_RegisterVariable (&cl_anglespeedkey);
   1088 	Cvar_RegisterVariable (&cl_shownet);
   1089 	Cvar_RegisterVariable (&cl_sbar);
   1090 	Cvar_RegisterVariable (&cl_hudswap);
   1091 	Cvar_RegisterVariable (&cl_maxfps);
   1092 	Cvar_RegisterVariable (&cl_timeout);
   1093 	Cvar_RegisterVariable (&lookspring);
   1094 	Cvar_RegisterVariable (&lookstrafe);
   1095 	Cvar_RegisterVariable (&sensitivity);
   1096 
   1097 	Cvar_RegisterVariable (&m_pitch);
   1098 	Cvar_RegisterVariable (&m_yaw);
   1099 	Cvar_RegisterVariable (&m_forward);
   1100 	Cvar_RegisterVariable (&m_side);
   1101 
   1102 	Cvar_RegisterVariable (&rcon_password);
   1103 	Cvar_RegisterVariable (&rcon_address);
   1104 
   1105 	Cvar_RegisterVariable (&entlatency);
   1106 	Cvar_RegisterVariable (&cl_predict_players2);
   1107 	Cvar_RegisterVariable (&cl_predict_players);
   1108 	Cvar_RegisterVariable (&cl_solid_players);
   1109 
   1110 	Cvar_RegisterVariable (&localid);
   1111 
   1112 	Cvar_RegisterVariable (&baseskin);
   1113 	Cvar_RegisterVariable (&noskins);
   1114 
   1115 	//
   1116 	// info mirrors
   1117 	//
   1118 	Cvar_RegisterVariable (&name);
   1119 	Cvar_RegisterVariable (&password);
   1120 	Cvar_RegisterVariable (&spectator);
   1121 	Cvar_RegisterVariable (&skin);
   1122 	Cvar_RegisterVariable (&team);
   1123 	Cvar_RegisterVariable (&topcolor);
   1124 	Cvar_RegisterVariable (&bottomcolor);
   1125 	Cvar_RegisterVariable (&rate);
   1126 	Cvar_RegisterVariable (&msg);
   1127 	Cvar_RegisterVariable (&noaim);
   1128 
   1129 
   1130 	Cmd_AddCommand ("version", CL_Version_f);
   1131 
   1132 	Cmd_AddCommand ("changing", CL_Changing_f);
   1133 	Cmd_AddCommand ("disconnect", CL_Disconnect_f);
   1134 	Cmd_AddCommand ("record", CL_Record_f);
   1135 	Cmd_AddCommand ("rerecord", CL_ReRecord_f);
   1136 	Cmd_AddCommand ("stop", CL_Stop_f);
   1137 	Cmd_AddCommand ("playdemo", CL_PlayDemo_f);
   1138 	Cmd_AddCommand ("timedemo", CL_TimeDemo_f);
   1139 
   1140 	Cmd_AddCommand ("skins", Skin_Skins_f);
   1141 	Cmd_AddCommand ("allskins", Skin_AllSkins_f);
   1142 
   1143 	Cmd_AddCommand ("quit", CL_Quit_f);
   1144 
   1145 	Cmd_AddCommand ("connect", CL_Connect_f);
   1146 	Cmd_AddCommand ("reconnect", CL_Reconnect_f);
   1147 
   1148 	Cmd_AddCommand ("rcon", CL_Rcon_f);
   1149 	Cmd_AddCommand ("packet", CL_Packet_f);
   1150 	Cmd_AddCommand ("user", CL_User_f);
   1151 	Cmd_AddCommand ("users", CL_Users_f);
   1152 
   1153 	Cmd_AddCommand ("setinfo", CL_SetInfo_f);
   1154 	Cmd_AddCommand ("fullinfo", CL_FullInfo_f);
   1155 	Cmd_AddCommand ("fullserverinfo", CL_FullServerinfo_f);
   1156 
   1157 	Cmd_AddCommand ("color", CL_Color_f);
   1158 	Cmd_AddCommand ("download", CL_Download_f);
   1159 
   1160 	Cmd_AddCommand ("nextul", CL_NextUpload);
   1161 	Cmd_AddCommand ("stopul", CL_StopUpload);
   1162 
   1163 //
   1164 // forward to server commands
   1165 //
   1166 	Cmd_AddCommand ("kill", NULL);
   1167 	Cmd_AddCommand ("pause", NULL);
   1168 	Cmd_AddCommand ("say", NULL);
   1169 	Cmd_AddCommand ("say_team", NULL);
   1170 	Cmd_AddCommand ("serverinfo", NULL);
   1171 
   1172 //
   1173 //  Windows commands
   1174 //
   1175 #ifdef _WINDOWS
   1176 	Cmd_AddCommand ("windows", CL_Windows_f);
   1177 #endif
   1178 }
   1179 
   1180 
   1181 /*
   1182 ================
   1183 Host_EndGame
   1184 
   1185 Call this to drop to a console without exiting the qwcl
   1186 ================
   1187 */
   1188 void Host_EndGame (char *message, ...)
   1189 {
   1190 	va_list		argptr;
   1191 	char		string[1024];
   1192 
   1193 	va_start (argptr,message);
   1194 	vsprintf (string,message,argptr);
   1195 	va_end (argptr);
   1196 	Con_Printf ("\n===========================\n");
   1197 	Con_Printf ("Host_EndGame: %s\n",string);
   1198 	Con_Printf ("===========================\n\n");
   1199 
   1200 	CL_Disconnect ();
   1201 
   1202 	longjmp (host_abort, 1);
   1203 }
   1204 
   1205 /*
   1206 ================
   1207 Host_Error
   1208 
   1209 This shuts down the client and exits qwcl
   1210 ================
   1211 */
   1212 void Host_Error (char *error, ...)
   1213 {
   1214 	va_list		argptr;
   1215 	char		string[1024];
   1216 	static	qboolean inerror = false;
   1217 
   1218 	if (inerror)
   1219 		Sys_Error ("Host_Error: recursively entered");
   1220 	inerror = true;
   1221 
   1222 	va_start (argptr,error);
   1223 	vsprintf (string,error,argptr);
   1224 	va_end (argptr);
   1225 	Con_Printf ("Host_Error: %s\n",string);
   1226 
   1227 	CL_Disconnect ();
   1228 	cls.demonum = -1;
   1229 
   1230 	inerror = false;
   1231 
   1232 // FIXME
   1233 	Sys_Error ("Host_Error: %s\n",string);
   1234 }
   1235 
   1236 
   1237 /*
   1238 ===============
   1239 Host_WriteConfiguration
   1240 
   1241 Writes key bindings and archived cvars to config.cfg
   1242 ===============
   1243 */
   1244 void Host_WriteConfiguration (void)
   1245 {
   1246 	FILE	*f;
   1247 
   1248 	if (host_initialized)
   1249 	{
   1250 		f = fopen (va("%s/config.cfg",com_gamedir), "w");
   1251 		if (!f)
   1252 		{
   1253 			Con_Printf ("Couldn't write config.cfg.\n");
   1254 			return;
   1255 		}
   1256 
   1257 		Key_WriteBindings (f);
   1258 		Cvar_WriteVariables (f);
   1259 
   1260 		fclose (f);
   1261 	}
   1262 }
   1263 
   1264 
   1265 //============================================================================
   1266 
   1267 #if 0
   1268 /*
   1269 ==================
   1270 Host_SimulationTime
   1271 
   1272 This determines if enough time has passed to run a simulation frame
   1273 ==================
   1274 */
   1275 qboolean Host_SimulationTime(float time)
   1276 {
   1277 	float fps;
   1278 
   1279 	if (oldrealtime > realtime)
   1280 		oldrealtime = 0;
   1281 
   1282 	if (cl_maxfps.value)
   1283 		fps = max(30.0, min(cl_maxfps.value, 72.0));
   1284 	else
   1285 		fps = max(30.0, min(rate.value/80.0, 72.0));
   1286 
   1287 	if (!cls.timedemo && (realtime + time) - oldrealtime < 1.0/fps)
   1288 		return false;			// framerate is too high
   1289 	return true;
   1290 }
   1291 #endif
   1292 
   1293 
   1294 /*
   1295 ==================
   1296 Host_Frame
   1297 
   1298 Runs all active servers
   1299 ==================
   1300 */
   1301 int		nopacketcount;
   1302 void Host_Frame (float time)
   1303 {
   1304 	static double		time1 = 0;
   1305 	static double		time2 = 0;
   1306 	static double		time3 = 0;
   1307 	int			pass1, pass2, pass3;
   1308 	float fps;
   1309 	if (setjmp (host_abort) )
   1310 		return;			// something bad happened, or the server disconnected
   1311 
   1312 	// decide the simulation time
   1313 	realtime += time;
   1314 	if (oldrealtime > realtime)
   1315 		oldrealtime = 0;
   1316 
   1317 	if (cl_maxfps.value)
   1318 		fps = max(30.0, min(cl_maxfps.value, 72.0));
   1319 	else
   1320 		fps = max(30.0, min(rate.value/80.0, 72.0));
   1321 
   1322 	if (!cls.timedemo && realtime - oldrealtime < 1.0/fps)
   1323 		return;			// framerate is too high
   1324 
   1325 	host_frametime = realtime - oldrealtime;
   1326 	oldrealtime = realtime;
   1327 	if (host_frametime > 0.2)
   1328 		host_frametime = 0.2;
   1329 
   1330 	// get new key events
   1331 	Sys_SendKeyEvents ();
   1332 
   1333 	// allow mice or other external controllers to add commands
   1334 	IN_Commands ();
   1335 
   1336 	// process console commands
   1337 	Cbuf_Execute ();
   1338 
   1339 	// fetch results from server
   1340 	CL_ReadPackets ();
   1341 
   1342 	// send intentions now
   1343 	// resend a connection request if necessary
   1344 	if (cls.state == ca_disconnected) {
   1345 		CL_CheckForResend ();
   1346 	} else
   1347 		CL_SendCmd ();
   1348 
   1349 	// Set up prediction for other players
   1350 	CL_SetUpPlayerPrediction(false);
   1351 
   1352 	// do client side motion prediction
   1353 	CL_PredictMove ();
   1354 
   1355 	// Set up prediction for other players
   1356 	CL_SetUpPlayerPrediction(true);
   1357 
   1358 	// build a refresh entity list
   1359 	CL_EmitEntities ();
   1360 
   1361 	// update video
   1362 	if (host_speeds.value)
   1363 		time1 = Sys_DoubleTime ();
   1364 
   1365 	SCR_UpdateScreen ();
   1366 
   1367 	if (host_speeds.value)
   1368 		time2 = Sys_DoubleTime ();
   1369 
   1370 	// update audio
   1371 	if (cls.state == ca_active)
   1372 	{
   1373 		S_Update (r_origin, vpn, vright, vup);
   1374 		CL_DecayLights ();
   1375 	}
   1376 	else
   1377 		S_Update (vec3_origin, vec3_origin, vec3_origin, vec3_origin);
   1378 
   1379 	CDAudio_Update();
   1380 
   1381 	if (host_speeds.value)
   1382 	{
   1383 		pass1 = (time1 - time3)*1000;
   1384 		time3 = Sys_DoubleTime ();
   1385 		pass2 = (time2 - time1)*1000;
   1386 		pass3 = (time3 - time2)*1000;
   1387 		Con_Printf ("%3i tot %3i server %3i gfx %3i snd\n",
   1388 					pass1+pass2+pass3, pass1, pass2, pass3);
   1389 	}
   1390 
   1391 	host_framecount++;
   1392 	fps_count++;
   1393 }
   1394 
   1395 static void simple_crypt(char *buf, int len)
   1396 {
   1397 	while (len--)
   1398 		*buf++ ^= 0xff;
   1399 }
   1400 
   1401 void Host_FixupModelNames(void)
   1402 {
   1403 	simple_crypt(emodel_name, sizeof(emodel_name) - 1);
   1404 	simple_crypt(pmodel_name, sizeof(pmodel_name) - 1);
   1405 	simple_crypt(prespawn_name,  sizeof(prespawn_name)  - 1);
   1406 	simple_crypt(modellist_name, sizeof(modellist_name) - 1);
   1407 	simple_crypt(soundlist_name, sizeof(soundlist_name) - 1);
   1408 }
   1409 
   1410 //============================================================================
   1411 
   1412 /*
   1413 ====================
   1414 Host_Init
   1415 ====================
   1416 */
   1417 void Host_Init (quakeparms_t *parms)
   1418 {
   1419 	COM_InitArgv (parms->argc, parms->argv);
   1420 	COM_AddParm ("-game");
   1421 	COM_AddParm ("qw");
   1422 
   1423 	Sys_mkdir("qw");
   1424 
   1425 	if (COM_CheckParm ("-minmemory"))
   1426 		parms->memsize = MINIMUM_MEMORY;
   1427 
   1428 	host_parms = *parms;
   1429 
   1430 	if (parms->memsize < MINIMUM_MEMORY)
   1431 		Sys_Error ("Only %4.1f megs of memory reported, can't execute game", parms->memsize / (float)0x100000);
   1432 
   1433 	Memory_Init (parms->membase, parms->memsize);
   1434 	Cbuf_Init ();
   1435 	Cmd_Init ();
   1436 	V_Init ();
   1437 
   1438 	COM_Init ();
   1439 
   1440 	Host_FixupModelNames();
   1441 
   1442 	NET_Init (PORT_CLIENT);
   1443 	Netchan_Init ();
   1444 
   1445 	W_LoadWadFile ("gfx.wad");
   1446 	Key_Init ();
   1447 	Con_Init ();
   1448 	M_Init ();
   1449 	Mod_Init ();
   1450 
   1451 //	Con_Printf ("Exe: "__TIME__" "__DATE__"\n");
   1452 	Con_Printf ("%4.1f megs RAM used.\n",parms->memsize/ (1024*1024.0));
   1453 
   1454 	R_InitTextures ();
   1455 
   1456 	host_basepal = (byte *)COM_LoadHunkFile ("gfx/palette.lmp");
   1457 	if (!host_basepal)
   1458 		Sys_Error ("Couldn't load gfx/palette.lmp");
   1459 	host_colormap = (byte *)COM_LoadHunkFile ("gfx/colormap.lmp");
   1460 	if (!host_colormap)
   1461 		Sys_Error ("Couldn't load gfx/colormap.lmp");
   1462 #ifdef __linux__
   1463 	IN_Init ();
   1464 	CDAudio_Init ();
   1465 	VID_Init (host_basepal);
   1466 	Draw_Init ();
   1467 	SCR_Init ();
   1468 	R_Init ();
   1469 
   1470 //	S_Init ();		// S_Init is now done as part of VID. Sigh.
   1471 
   1472 	cls.state = ca_disconnected;
   1473 	Sbar_Init ();
   1474 	CL_Init ();
   1475 #else
   1476 	VID_Init (host_basepal);
   1477 	Draw_Init ();
   1478 	SCR_Init ();
   1479 	R_Init ();
   1480 //	S_Init ();		// S_Init is now done as part of VID. Sigh.
   1481 #ifdef GLQUAKE
   1482 	S_Init();
   1483 #endif
   1484 
   1485 	cls.state = ca_disconnected;
   1486 	CDAudio_Init ();
   1487 	Sbar_Init ();
   1488 	CL_Init ();
   1489 	IN_Init ();
   1490 #endif
   1491 
   1492 	Cbuf_InsertText ("exec quake.rc\n");
   1493 	Cbuf_AddText ("echo Type connect <internet address> or use GameSpy to connect to a game.\n");
   1494 	Cbuf_AddText ("cl_warncmd 1\n");
   1495 
   1496 	Hunk_AllocName (0, "-HOST_HUNKLEVEL-");
   1497 	host_hunklevel = Hunk_LowMark ();
   1498 
   1499 	host_initialized = true;
   1500 
   1501 	Con_Printf ("\nClient Version %4.2f (Build %04d)\n\n", VERSION, build_number());
   1502 
   1503 	Con_Printf (" QuakeWorld Initialized \n");
   1504 }
   1505 
   1506 
   1507 /*
   1508 ===============
   1509 Host_Shutdown
   1510 
   1511 FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
   1512 to run quit through here before the final handoff to the sys code.
   1513 ===============
   1514 */
   1515 void Host_Shutdown(void)
   1516 {
   1517 	static qboolean isdown = false;
   1518 
   1519 	if (isdown)
   1520 	{
   1521 		printf ("recursive shutdown\n");
   1522 		return;
   1523 	}
   1524 	isdown = true;
   1525 
   1526 	Host_WriteConfiguration ();
   1527 
   1528 	CDAudio_Shutdown ();
   1529 	NET_Shutdown ();
   1530 	S_Shutdown();
   1531 	IN_Shutdown ();
   1532 	if (host_basepal)
   1533 		VID_Shutdown();
   1534 }
   1535 
   1536