1/*
2Copyright (C) 1996-1997 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
34qboolean	noclip_anglehack;		// remnant from old quake
35
36
37cvar_t	rcon_password = CVAR3("rcon_password", "", false);
38
39cvar_t	rcon_address = CVAR2("rcon_address", "");
40
41cvar_t	cl_timeout = CVAR2("cl_timeout", "60");
42
43cvar_t	cl_shownet = CVAR2("cl_shownet","0");	// can be 0, 1, or 2
44
45cvar_t	cl_sbar		= CVAR3("cl_sbar", "0", true);
46cvar_t	cl_hudswap	= CVAR3("cl_hudswap", "0", true);
47cvar_t	cl_maxfps	= CVAR3("cl_maxfps", "0", true);
48
49cvar_t	lookspring = CVAR3("lookspring","0", true);
50cvar_t	lookstrafe = CVAR3("lookstrafe","0", true);
51cvar_t	sensitivity = CVAR3("sensitivity","3", true);
52
53cvar_t	m_pitch = CVAR3("m_pitch","0.022", true);
54cvar_t	m_yaw = CVAR2("m_yaw","0.022");
55cvar_t	m_forward = CVAR2("m_forward","1");
56cvar_t	m_side = CVAR2("m_side","0.8");
57
58cvar_t	entlatency = CVAR2("entlatency", "20");
59cvar_t	cl_predict_players = CVAR2("cl_predict_players", "1");
60cvar_t	cl_predict_players2 = CVAR2("cl_predict_players2", "1");
61cvar_t	cl_solid_players = CVAR2("cl_solid_players", "1");
62
63cvar_t  localid = CVAR2("localid", "");
64
65static qboolean allowremotecmd = true;
66
67//
68// info mirrors
69//
70cvar_t	password = CVAR4("password", "", false, true);
71cvar_t	spectator = CVAR4("spectator", "", false, true);
72cvar_t	name = CVAR4("name","unnamed", true, true);
73cvar_t	team = CVAR4("team","", true, true);
74cvar_t	skin = CVAR4("skin","", true, true);
75cvar_t	topcolor = CVAR4("topcolor","0", true, true);
76cvar_t	bottomcolor = CVAR4("bottomcolor","0", true, true);
77cvar_t	rate = CVAR4("rate","2500", true, true);
78cvar_t	noaim = CVAR4("noaim","0", true, true);
79cvar_t	msg = CVAR4("msg","1", true, true);
80
81extern cvar_t cl_hightrack;
82
83
84client_static_t	cls;
85client_state_t	cl;
86
87entity_state_t	cl_baselines[MAX_EDICTS];
88efrag_t			cl_efrags[MAX_EFRAGS];
89entity_t		cl_static_entities[MAX_STATIC_ENTITIES];
90lightstyle_t	cl_lightstyle[MAX_LIGHTSTYLES];
91dlight_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
96int				cl_numvisedicts, cl_oldnumvisedicts;
97entity_t		*cl_visedicts, *cl_oldvisedicts;
98entity_t		cl_visedicts_list[2][MAX_VISEDICTS];
99
100double			connect_time = -1;		// for connection retransmits
101
102quakeparms_t host_parms;
103
104qboolean	host_initialized;		// true if into command execution
105qboolean	nomaster;
106
107double		host_frametime;
108double		realtime;				// without any filtering or bounding
109double		oldrealtime;			// last frame run
110int			host_framecount;
111
112int			host_hunklevel;
113
114byte		*host_basepal;
115byte		*host_colormap;
116
117netadr_t	master_adr;				// address of the master server
118
119cvar_t	host_speeds = CVAR2("host_speeds","0");			// set for running times
120cvar_t	show_fps = CVAR2("show_fps","0");			// set for running times
121cvar_t	developer = CVAR2("developer","0");
122
123int			fps_count;
124
125jmp_buf 	host_abort;
126
127void Master_Connect_f (void);
128
129float	server_version = 0;	// version of server we connected to
130
131char emodel_name[] =
132	{ 'e' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
133char pmodel_name[] =
134	{ 'p' ^ 0xff, 'm' ^ 0xff, 'o' ^ 0xff, 'd' ^ 0xff, 'e' ^ 0xff, 'l' ^ 0xff, 0 };
135char 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 };
138char 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 };
141char 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==================
147CL_Quit_f
148==================
149*/
150void 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=======================
163CL_Version_f
164======================
165*/
166void 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=======================
175CL_SendConnectPacket
176
177called by CL_Connect_f and CL_CheckResend
178======================
179*/
180void 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=================
226CL_CheckForResend
227
228Resend a connect message if the last one has timed out
229
230=================
231*/
232void 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
270void CL_BeginServerConnect(void)
271{
272	connect_time = 0;
273	CL_CheckForResend();
274}
275
276/*
277================
278CL_Connect_f
279
280================
281*/
282void 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=====================
303CL_Rcon_f
304
305  Send the rest of the command line over as
306  an unconnected command.
307=====================
308*/
309void 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=====================
361CL_ClearState
362
363=====================
364*/
365void 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=====================
400CL_Disconnect
401
402Sends a disconnect message to the server
403This is also called on Host_Error, so it shouldn't cause any errors
404=====================
405*/
406void 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
448void CL_Disconnect_f (void)
449{
450	CL_Disconnect ();
451}
452
453/*
454====================
455CL_User_f
456
457user <name or userid>
458
459Dump userdata / masterdata for a user
460====================
461*/
462void 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====================
491CL_Users_f
492
493Dump userids for all current players
494====================
495*/
496void 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
516void 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==================
554CL_FullServerinfo_f
555
556Sent by server when serverinfo changes
557==================
558*/
559void 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==================
584CL_FullInfo_f
585
586Allow clients to change userinfo
587==================
588Casey was here :)
589*/
590void 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==================
637CL_SetInfo_f
638
639Allow clients to change userinfo
640==================
641*/
642void 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====================
664CL_Packet_f
665
666packet <destination> <contents>
667
668Contents allows \n escape character
669====================
670*/
671void 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=====================
713CL_NextDemo
714
715Called to play the next demo in the demo loop
716=====================
717*/
718void 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=================
744CL_Changing_f
745
746Just sent as a hint to the client that they should
747drop to full console
748=================
749*/
750void 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=================
764CL_Reconnect_f
765
766The server is changing levels
767=================
768*/
769void 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=================
794CL_ConnectionlessPacket
795
796Responses to broadcasts, etc
797=================
798*/
799void 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=================
932CL_ReadPackets
933=================
934*/
935void 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=====================
990CL_Download_f
991=====================
992*/
993void 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=================
1034CL_Minimize_f
1035=================
1036*/
1037void 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=================
1047CL_Init
1048=================
1049*/
1050void 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================
1183Host_EndGame
1184
1185Call this to drop to a console without exiting the qwcl
1186================
1187*/
1188void 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================
1207Host_Error
1208
1209This shuts down the client and exits qwcl
1210================
1211*/
1212void 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===============
1239Host_WriteConfiguration
1240
1241Writes key bindings and archived cvars to config.cfg
1242===============
1243*/
1244void 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==================
1270Host_SimulationTime
1271
1272This determines if enough time has passed to run a simulation frame
1273==================
1274*/
1275qboolean 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==================
1296Host_Frame
1297
1298Runs all active servers
1299==================
1300*/
1301int		nopacketcount;
1302void 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
1395static void simple_crypt(char *buf, int len)
1396{
1397	while (len--)
1398		*buf++ ^= 0xff;
1399}
1400
1401void 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====================
1414Host_Init
1415====================
1416*/
1417void 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===============
1509Host_Shutdown
1510
1511FIXME: this is a callback from Sys_Quit and Sys_Error.  It would be better
1512to run quit through here before the final handoff to the sys code.
1513===============
1514*/
1515void 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