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 #ifndef __TIMESTAMP__
25 #define __TIMESTAMP__ __DATE__ " " __TIME__
26 #endif
27 
28 int deferred_model_index;
29 
30 extern cvar_t	*qport;
31 extern cvar_t	*vid_ref;
32 
33 typedef struct incoming_s
34 {
35 	netadr_t	remote;
36 	int			type;
37 	uint32		time;
38 } incoming_t;
39 
40 #define	CL_UNDEF				0
41 #define	CL_RCON					1
42 #define	CL_SERVER_INFO			2
43 #define	CL_SERVER_STATUS		3
44 #define	CL_SERVER_STATUS_FULL	4
45 #define CL_MASTER_QUERY			5
46 
47 //list of addresses other than remote that can send us connectionless and for what purpose
48 static incoming_t	incoming_allowed[16];
49 static int			incoming_allowed_index;
50 
51 typedef struct ignore_s
52 {
53 	struct ignore_s	*next;
54 	char			*text;
55 } ignore_t;
56 
57 ignore_t	cl_ignores;
58 
59 cvar_t	*freelook;
60 
61 cvar_t	*adr0;
62 cvar_t	*adr1;
63 cvar_t	*adr2;
64 cvar_t	*adr3;
65 cvar_t	*adr4;
66 cvar_t	*adr5;
67 cvar_t	*adr6;
68 cvar_t	*adr7;
69 cvar_t	*adr8;
70 
71 #ifdef CL_STEREO_SUPPORT
72 cvar_t	*cl_stereo_separation;
73 cvar_t	*cl_stereo;
74 #endif
75 
76 cvar_t	*rcon_client_password;
77 cvar_t	*rcon_address;
78 
79 cvar_t	*cl_noskins;
80 //cvar_t	*cl_autoskins;
81 cvar_t	*cl_footsteps;
82 cvar_t	*cl_timeout;
83 cvar_t	*cl_predict;
84 cvar_t	*cl_backlerp;
85 cvar_t	*r_maxfps;
86 cvar_t	*cl_maxfps;
87 cvar_t	*cl_async;
88 cvar_t	*cl_smoothsteps;
89 cvar_t	*cl_instantpacket;
90 
91 cvar_t	*cl_gun;
92 
93 cvar_t	*cl_add_particles;
94 cvar_t	*cl_add_lights;
95 cvar_t	*cl_add_entities;
96 cvar_t	*cl_add_blend;
97 
98 cvar_t	*cl_shownet;
99 cvar_t	*cl_showmiss;
100 cvar_t	*cl_showclamp;
101 
102 cvar_t	*cl_paused;
103 cvar_t	*cl_timedemo;
104 
105 //r1: filter high bits
106 cvar_t	*cl_filterchat;
107 
108 cvar_t	*lookspring;
109 cvar_t	*lookstrafe;
110 cvar_t	*sensitivity;
111 
112 cvar_t	*m_pitch;
113 cvar_t	*m_yaw;
114 cvar_t	*m_forward;
115 cvar_t	*m_side;
116 
117 cvar_t	*cl_lightlevel;
118 
119 //
120 // userinfo
121 //
122 cvar_t	*info_password;
123 cvar_t	*info_spectator;
124 cvar_t	*name;
125 cvar_t	*skin;
126 cvar_t	*rate;
127 cvar_t	*fov;
128 cvar_t	*msg;
129 cvar_t	*hand;
130 cvar_t	*gender;
131 cvar_t	*gender_auto;
132 
133 cvar_t	*cl_vwep;
134 
135 //r1ch
136 //cvar_t	*cl_defertimer;
137 cvar_t	*cl_protocol;
138 
139 cvar_t	*dbg_framesleep;
140 //cvar_t	*cl_snaps;
141 
142 cvar_t	*cl_nolerp;
143 
144 cvar_t	*cl_instantack;
145 cvar_t	*cl_autorecord;
146 
147 cvar_t	*cl_railtrail;
148 cvar_t	*cl_test = &uninitialized_cvar;
149 cvar_t	*cl_test2;
150 cvar_t	*cl_test3;
151 
152 cvar_t	*cl_original_dlights;
153 cvar_t	*cl_default_location = &uninitialized_cvar;
154 cvar_t	*cl_player_updates;
155 cvar_t	*cl_updaterate;
156 
157 cvar_t	*cl_proxy;
158 
159 #ifdef NO_SERVER
160 cvar_t	*allow_download;
161 cvar_t *allow_download_players;
162 cvar_t *allow_download_models;
163 cvar_t *allow_download_sounds;
164 cvar_t *allow_download_maps;
165 #endif
166 
167 client_static_t	cls;
168 client_state_t	cl;
169 
170 centity_t		cl_entities[MAX_EDICTS];
171 
172 entity_state_t	cl_parse_entities[MAX_PARSE_ENTITIES];
173 
174 qboolean	send_packet_now;
175 
176 extern	cvar_t *allow_download;
177 extern	cvar_t *allow_download_players;
178 extern	cvar_t *allow_download_models;
179 extern	cvar_t *allow_download_sounds;
180 extern	cvar_t *allow_download_maps;
181 
182 //======================================================================
183 
184 /*
185 ====================
186 CL_WriteDemoMessage
187 
188 Dumps the current net message, prefixed by the length
189 ====================
190 */
CL_WriteDemoMessageFull(void)191 void CL_WriteDemoMessageFull (void)
192 {
193 	int		len, swlen;
194 
195 	// the first eight bytes are just packet sequencing stuff
196 	len = net_message.cursize-8;
197 	swlen = LittleLong(len);
198 	if (swlen > 0)
199 	{
200 		fwrite (&swlen, 4, 1, cls.demofile);
201 		fwrite (net_message_buffer+8, len, 1, cls.demofile);
202 	}
203 }
204 
CL_WriteDemoMessage(byte * buff,int len,qboolean forceFlush)205 void CL_WriteDemoMessage (byte *buff, int len, qboolean forceFlush)
206 {
207 	if (!cls.demorecording || cls.serverProtocol == PROTOCOL_ORIGINAL)
208 		return;
209 
210 	if (forceFlush)
211 	{
212 		if (!cls.demowaiting)
213 		{
214 			qboolean	dropped_frame;
215 			int			swlen;
216 
217 			dropped_frame = false;
218 
219 			if (cl.demoBuff.overflowed)
220 			{
221 				Com_DPrintf ("Dropped a demo frame, maximum message size exceeded: %d > %d\n", cl.demoBuff.cursize, cl.demoBuff.maxsize);
222 
223 				//we write a message regardless to keep in sync time-wise.
224 				SZ_Clear (&cl.demoBuff);
225 				SZ_WriteByte (&cl.demoBuff, svc_nop);
226 				dropped_frame = true;
227 			}
228 			else if (!cl.demoBuff.cursize)
229 			{
230 				Com_DPrintf ("Dropped a demo frame, no data to be written\n");
231 				//never write zero length frames, they cause demo end
232 				//we write a message regardless to keep in sync time-wise.
233 				SZ_Clear (&cl.demoBuff);
234 				SZ_WriteByte (&cl.demoBuff, svc_nop);
235 				dropped_frame = true;
236 			}
237 
238 			swlen = LittleLong(cl.demoBuff.cursize);
239 			fwrite (&swlen, 4, 1, cls.demofile);
240 			fwrite (cl.demoFrame, cl.demoBuff.cursize, 1, cls.demofile);
241 
242 			//fixme: this is ugly
243 			if (noFrameFromServerPacket == 0 && !dropped_frame)
244 				cl.demoLastFrame = &cl.frames[cl.frame.serverframe & UPDATE_MASK];
245 		}
246 		SZ_Clear (&cl.demoBuff);
247 	}
248 
249 	if (len)
250 		SZ_Write (&cl.demoBuff, buff, len);
251 }
252 
253 void CL_DemoDeltaEntity (const entity_state_t *from, const entity_state_t *to, qboolean force, qboolean newentity);
CL_BeginRecording(char * name)254 qboolean CL_BeginRecording (char *name)
255 {
256 	byte	buf_data[1390];
257 	sizebuf_t	buf;
258 	int		i;
259 	int		len;
260 	entity_state_t	*ent;
261 
262 	FS_CreatePath (name);
263 
264 	cls.demofile = fopen (name, "wb");
265 
266 	if (!cls.demofile)
267 		return false;
268 
269 	cls.demorecording = true;
270 
271 	// don't start saving messages until a non-delta compressed message is received
272 	cls.demowaiting = true;
273 
274 	// inform server we need to receive more data
275 	if (cls.serverProtocol == PROTOCOL_R1Q2)
276 	{
277 		MSG_BeginWriting (clc_setting);
278 		MSG_WriteShort (CLSET_RECORDING);
279 		MSG_WriteShort (1);
280 		MSG_EndWriting (&cls.netchan.message);
281 	}
282 
283 	//
284 	// write out messages to hold the startup information
285 	//
286 	SZ_Init (&buf, buf_data, sizeof(buf_data));
287 
288 	//if (cls.serverProtocol == PROTOCOL_ORIGINAL)
289 	//	buf.maxsize = 1390;
290 
291 	// send the serverdata
292 	MSG_BeginWriting (svc_serverdata);
293 	MSG_WriteLong (PROTOCOL_ORIGINAL);
294 	MSG_WriteLong (0x10000 + cl.servercount);
295 	MSG_WriteByte (1);	// demos are always attract loops
296 	MSG_WriteString (cl.gamedir);
297 	MSG_WriteShort (cl.playernum);
298 	MSG_WriteString (cl.configstrings[CS_NAME]);
299 	/*if (cls.serverProtocol == PROTOCOL_R1Q2)
300 	{
301 		MSG_WriteByte (cl.enhancedServer);
302 		MSG_WriteShort (MINOR_VERSION_R1Q2);
303 		MSG_WriteByte (cl.advancedDeltas);
304 	}*/
305 	MSG_EndWriting (&buf);
306 
307 	// configstrings
308 	for (i=0 ; i<MAX_CONFIGSTRINGS ; i++)
309 	{
310 		if (cl.configstrings[i][0])
311 		{
312 			if (buf.cursize + strlen (cl.configstrings[i]) + 64 > buf.maxsize)
313 			{	// write it out
314 				len = LittleLong (buf.cursize);
315 				fwrite (&len, 4, 1, cls.demofile);
316 				fwrite (buf.data, buf.cursize, 1, cls.demofile);
317 				buf.cursize = 0;
318 			}
319 
320 			MSG_BeginWriting (svc_configstring);
321 			MSG_WriteShort (i);
322 			MSG_WriteString (cl.configstrings[i]);
323 			MSG_EndWriting (&buf);
324 		}
325 	}
326 
327 	// baselines
328 
329 	for (i=0; i<MAX_EDICTS ; i++)
330 	{
331 		ent = &cl_entities[i].baseline;
332 		if (!ent->modelindex)
333 			continue;
334 
335 		if (buf.cursize + 64 > buf.maxsize)
336 		{	// write it out
337 			len = LittleLong (buf.cursize);
338 			fwrite (&len, 4, 1, cls.demofile);
339 			fwrite (buf.data, buf.cursize, 1, cls.demofile);
340 			buf.cursize = 0;
341 		}
342 
343 		MSG_BeginWriting (svc_spawnbaseline);
344 		CL_DemoDeltaEntity (&null_entity_state, &cl_entities[i].baseline, true, true);
345 		MSG_EndWriting (&buf);
346 	}
347 
348 	MSG_BeginWriting (svc_stufftext);
349 	MSG_WriteString ("precache\n");
350 	MSG_EndWriting (&buf);
351 
352 	// write it to the demo file
353 
354 	len = LittleLong (buf.cursize);
355 	fwrite (&len, 4, 1, cls.demofile);
356 	fwrite (buf.data, buf.cursize, 1, cls.demofile);
357 
358 	// the rest of the demo file will be individual frames
359 	return true;
360 }
361 
CL_EndRecording(void)362 void CL_EndRecording(void)
363 {
364 	int len;
365 
366 	// finish up
367 	len = -1;
368 	fwrite (&len, 4, 1, cls.demofile);
369 	fclose (cls.demofile);
370 
371 	// inform server we are done with extra data
372 	if (cls.serverProtocol == PROTOCOL_R1Q2)
373 	{
374 		MSG_BeginWriting (clc_setting);
375 		MSG_WriteShort (CLSET_RECORDING);
376 		MSG_WriteShort (0);
377 		MSG_EndWriting (&cls.netchan.message);
378 	}
379 
380 	cls.demofile = NULL;
381 	cls.demorecording = false;
382 
383 	// reset delta demo state
384 	SZ_Clear (&cl.demoBuff);
385 	cl.demoLastFrame = NULL;
386 }
387 
388 /*
389 ====================
390 CL_Stop_f
391 
392 stop recording a demo
393 ====================
394 */
CL_Stop_f(void)395 void CL_Stop_f (void)
396 {
397 	int		len;
398 
399 	if (!cls.demorecording)
400 	{
401 		Com_Printf ("Not recording a demo.\n", LOG_CLIENT);
402 		return;
403 	}
404 
405 	len = ftell (cls.demofile);
406 
407 	CL_EndRecording();
408 
409 	Com_Printf ("Stopped demo, recorded %d bytes.\n", LOG_CLIENT, len);
410 }
411 
CL_Ignore_f(void)412 void CL_Ignore_f (void)
413 {
414 	ignore_t	*list, *last, *newentry;
415 
416 	if (Cmd_Argc() < 2)
417 	{
418 		Com_Printf ("usage: ignore text\n", LOG_GENERAL);
419 		return;
420 	}
421 
422 	list = &cl_ignores;
423 
424 	last = list->next;
425 
426 	newentry = Z_TagMalloc (sizeof(*list), TAGMALLOC_CLIENT_IGNORE);
427 	newentry->text = CopyString (Cmd_Args(), TAGMALLOC_CLIENT_IGNORE);
428 	newentry->next = last;
429 	list->next = newentry;
430 
431 	Com_Printf ("%s added to ignore list.\n", LOG_GENERAL, newentry->text);
432 }
433 
CL_IgnoreMatch(const char * string)434 qboolean CL_IgnoreMatch (const char *string)
435 {
436 	ignore_t	*entry;
437 
438 	entry = &cl_ignores;
439 
440 	while (entry->next)
441 	{
442 		entry = entry->next;
443 		if (strstr (string, entry->text))
444 			return true;
445 	}
446 
447 	return false;
448 }
449 
CL_Unignore_f(void)450 void CL_Unignore_f (void)
451 {
452 	ignore_t	*entry, *last;
453 	char		*match;
454 
455 	if (Cmd_Argc() < 2)
456 	{
457 		Com_Printf ("usage: unignore text\n", LOG_GENERAL);
458 		return;
459 	}
460 
461 	match = Cmd_Args();
462 	entry = last = &cl_ignores;
463 
464 	while (entry->next)
465 	{
466 		entry = entry->next;
467 
468 		if (!Q_stricmp (match, entry->text))
469 		{
470 			last->next = entry->next;
471 			Z_Free (entry->text);
472 			Z_Free (entry);
473 
474 			Com_Printf ("Ignore '%s' removed.\n", LOG_GENERAL, match);
475 			return;
476 		}
477 		last = entry;
478 	}
479 
480 	Com_Printf ("Ignore '%s' not found.\n", LOG_GENERAL, match);
481 }
482 
483 /*
484 ====================
485 CL_Record_f
486 
487 record <demoname>
488 
489 Begins recording a demo from the current position
490 ====================
491 */
CL_Record_f(void)492 void CL_Record_f (void)
493 {
494 	char	name[MAX_OSPATH];
495 
496 	if (Cmd_Argc() != 2)
497 	{
498 		Com_Printf ("record <demoname>\n", LOG_CLIENT);
499 		return;
500 	}
501 
502 	if (cls.demorecording)
503 	{
504 		Com_Printf ("Already recording.\n", LOG_CLIENT);
505 		return;
506 	}
507 
508 	if (cls.state != ca_active)
509 	{
510 		Com_Printf ("You must be in a level to record.\n", LOG_CLIENT);
511 		return;
512 	}
513 
514 	if (cl.attractloop)
515 	{
516 		Com_Printf ("Unable to record from a demo stream due to insufficient deltas.\n", LOG_CLIENT);
517 		return;
518 	}
519 
520 	if (strstr (Cmd_Argv(1), "..") || strchr (Cmd_Argv(1), '/') || strchr (Cmd_Argv(1), '\\') )
521 	{
522 		Com_Printf ("Illegal filename.\n", LOG_CLIENT);
523 		return;
524 	}
525 
526 	//
527 	// open the demo file
528 	//
529 	Com_sprintf (name, sizeof(name), "%s/demos/%s.dm2", FS_Gamedir(), Cmd_Argv(1));
530 
531 	FS_CreatePath (name);
532 
533 	if (!CL_BeginRecording (name))
534 	{
535 		Com_Printf ("ERROR: Couldn't open %s for writing.\n", LOG_CLIENT, name);
536 	}
537 	else
538 	{
539 		/*if (cls.serverProtocol == PROTOCOL_R1Q2)
540 		{
541 			//make it a bit more obvious for the bleeding idiots out there
542 			//Com_Printf ("WARNING: Demos recorded at cl_protocol 35 are not guaranteed to replay properly!\n", LOG_CLIENT);
543 			Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n", LOG_CLIENT);
544 			S_StartLocalSound ("player/burn1.wav");
545 			S_StartLocalSound ("player/burn2.wav");
546 			con.ormask = 128;
547 			Com_Printf ("Demos recorded at cl_protocol 35 are\nnot guaranteed to replay properly!\n", LOG_CLIENT);
548 			con.ormask = 0;
549 			Com_Printf("\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n", LOG_CLIENT);
550 		}*/
551 		Com_Printf ("recording to %s.\n", LOG_CLIENT, name);
552 	}
553 }
554 
555 //======================================================================
556 
557 /*
558 ===================
559 Cmd_ForwardToServer
560 
561 adds the current command line as a clc_stringcmd to the client message.
562 things like godmode, noclip, etc, are commands directed to the server,
563 so when they are typed in at the console, they will need to be forwarded.
564 ===================
565 */
Cmd_ForwardToServer(void)566 void Cmd_ForwardToServer (void)
567 {
568 	char	*cmd;
569 
570 	cmd = Cmd_Argv(0);
571 	if (cls.state <= ca_connected || *cmd == '-' || *cmd == '+')
572 	{
573 		Com_Printf ("Unknown command \"%s\"\n", LOG_CLIENT, cmd);
574 		return;
575 	}
576 
577 	Com_DPrintf ("Cmd_ForwardToServer: forwarding '%s'\n", cmd);
578 
579 	//Com_Printf ("fwd: %s %s\n", cmd, Cmd_Args());
580 
581 	MSG_BeginWriting (clc_stringcmd);
582 	MSG_Print (cmd);
583 	if (Cmd_Argc() > 1)
584 	{
585 		MSG_Print (" ");
586 		MSG_Print (Cmd_Args());
587 	}
588 	MSG_EndWriting (&cls.netchan.message);
589 	send_packet_now = true;
590 }
591 
592 /*void CL_Setenv_f( void )
593 {
594 	int argc = Cmd_Argc();
595 
596 	if ( argc > 2 )
597 	{
598 		char buffer[1000];
599 		int i;
600 
601 		strcpy( buffer, Cmd_Argv(1) );
602 		strcat( buffer, "=" );
603 
604 		for ( i = 2; i < argc; i++ )
605 		{
606 			strcat( buffer, Cmd_Argv( i ) );
607 			strcat( buffer, " " );
608 		}
609 
610 		putenv( buffer );
611 	}
612 	else if ( argc == 2 )
613 	{
614 		char *env = getenv( Cmd_Argv(1) );
615 
616 		if ( env )
617 		{
618 			Com_Printf( "%s=%s\n", Cmd_Argv(1), env );
619 		}
620 		else
621 		{
622 			Com_Printf( "%s undefined\n", Cmd_Argv(1), env );
623 		}
624 	}
625 }*/
626 
627 /*
628 ==================
629 CL_ForwardToServer_f
630 ==================
631 */
CL_ForwardToServer_f(void)632 void CL_ForwardToServer_f (void)
633 {
634 	//if (cls.state != ca_connected && cls.state != ca_active)
635 	if (cls.state < ca_connected)
636 	{
637 		Com_Printf ("Can't \"%s\", not connected\n", LOG_CLIENT, Cmd_Argv(0));
638 		return;
639 	}
640 
641 	//r1: support \xxx escape sequences for testing various evil hacks
642 #ifdef _DEBUG
643 	{
644 		char *rew = Cmd_Args();
645 		char args[MAX_STRING_CHARS];
646 		char tmp[4];
647 		memset (args, 0, sizeof(args));
648 		strcpy (args, rew);
649 		memset (rew, 0, sizeof(args));
650 		strcpy (rew, args);
651 		while (*rew && *rew+3) {
652 			if (*rew == '\\' && isdigit(*(rew+1)) && isdigit(*(rew+2)) && isdigit(*(rew+3))) {
653 				Q_strncpy (tmp, rew+1, 3);
654 				*rew = atoi(tmp);
655 				memcpy (rew+1, rew+4, 1024 - (rew - Cmd_Args() + 3));
656 				//*(rew + (rew - Cmd_Args() - 2)) = 0;
657 			}
658 			rew++;
659 		}
660 	}
661 #endif
662 
663 	//Com_Printf ("fwd: %s\n", Cmd_Args());
664 
665 	// don't forward the first argument
666 	if (Cmd_Argc() > 1)
667 	{
668 		Com_DPrintf ("CL_ForwardToServer_f: Wrote '%s'\n", Cmd_Args());
669 
670 		MSG_WriteByte (clc_stringcmd);
671 		MSG_Print (Cmd_Args());
672 		MSG_EndWriting (&cls.netchan.message);
673 
674 		send_packet_now = true;
675 	}
676 }
677 
678 
679 /*
680 ==================
681 CL_Pause_f
682 ==================
683 */
CL_Pause_f(void)684 void CL_Pause_f (void)
685 {
686 	// never pause in multiplayer
687 	if (!cl.attractloop && (Cvar_IntValue ("maxclients") > 1 || !Com_ServerState ()))
688 	{
689 		Cvar_Set ("paused", "0");
690 		return;
691 	}
692 
693 	Cvar_SetValue ("paused", (float)!cl_paused->intvalue);
694 }
695 
696 /*
697 ==================
698 CL_Quit_f
699 ==================
700 */
CL_Quit_f(void)701 NORETURN void CL_Quit_f (void)
702 {
703 	CL_Disconnect (false);
704 	Com_Quit ();
705 }
706 
707 /*
708 ================
709 CL_Drop
710 
711 Called after an ERR_DROP was thrown
712 ================
713 */
CL_Drop(qboolean skipdisconnect,qboolean nonerror)714 void CL_Drop (qboolean skipdisconnect, qboolean nonerror)
715 {
716 	if (cls.state == ca_uninitialized)
717 		return;
718 
719 	if (cls.state == ca_disconnected)
720 		return;
721 
722 	CL_Disconnect (skipdisconnect);
723 
724 	// drop loading plaque unless this is the initial game start
725 	if (cls.disable_servercount != -1)
726 		SCR_EndLoadingPlaque ();	// get rid of loading plaque
727 
728 	//reset if we crashed a menu
729 	M_ForceMenuOff ();
730 
731 	if (!nonerror)
732 		cls.serverProtocol = 0;
733 }
734 
735 
736 /*
737 =======================
738 CL_SendConnectPacket
739 
740 We have gotten a challenge from the server, so try and
741 connect.
742 ======================
743 */
CL_SendConnectPacket(int useProtocol)744 void CL_SendConnectPacket (int useProtocol)
745 {
746 	netadr_t	adr;
747 	int			port;
748 	unsigned	msglen;
749 
750 #ifdef CLIENT_DLL
751 	if (!cllib_active) {
752 		//retry loading it now
753 		CL_ClDLL_Restart_f ();
754 		//if (!cllib_active)
755 			//Com_Error (ERR_DROP, "no client_dll.dll loaded for current game.");
756 	}
757 #endif
758 
759 	if (!NET_StringToAdr (cls.servername, &adr))
760 	{
761 		Com_Printf ("Bad server address: %s\n", LOG_CLIENT, cls.servername);
762 		cls.connect_time = 0;
763 		return;
764 	}
765 
766 	if (adr.port == 0)
767 		adr.port = ShortSwap (PORT_SERVER);
768 
769 	if (qport->intvalue == -1 || cls.proxyState == ps_active)
770 		port = (int)(random() * 0xFFFF);
771 	else
772 		port = qport->intvalue;
773 
774 	userinfo_modified = false;
775 
776 	if (Com_ServerState() == ss_demo)
777 	{
778 		msglen = MAX_USABLEMSG;
779 		cls.serverProtocol = 35;
780 	}
781 	else
782 	{
783 		msglen = Cvar_IntValue ("net_maxmsglen");
784 
785 		if (!cls.serverProtocol)
786 		{
787 			if (cl_protocol->intvalue)
788 				cls.serverProtocol = cl_protocol->intvalue;
789 			else if (useProtocol)
790 				cls.serverProtocol = useProtocol;
791 			else
792 				cls.serverProtocol = PROTOCOL_R1Q2;
793 		}
794 	}
795 
796 	if (cls.serverProtocol == PROTOCOL_R1Q2)
797 		port &= 0xFF;
798 
799 	cls.quakePort = port;
800 
801 	Com_DPrintf ("Cl_SendConnectPacket: protocol=%d, port=%d, challenge=%u\n", cls.serverProtocol, port, cls.challenge);
802 
803 	//r1: only send enhanced connect string on new protocol in order to avoid confusing
804 	//    other engine mods which may or may not extend this.
805 
806 
807 	if (cls.serverProtocol == PROTOCOL_R1Q2)
808 		Netchan_OutOfBandPrint (NS_CLIENT, &adr, "connect %i %i %i \"%s\" %u %u\n", cls.serverProtocol, port, cls.challenge, Cvar_Userinfo(), msglen, MINOR_VERSION_R1Q2);
809 	else
810 		Netchan_OutOfBandPrint (NS_CLIENT, &adr, "connect %i %i %i \"%s\"\n", cls.serverProtocol, port, cls.challenge, Cvar_Userinfo());
811 }
812 
813 /*
814 =================
815 CL_Reconnect_f
816 
817 The server is changing levels
818 =================
819 */
CL_Reconnect_f(void)820 void CL_Reconnect_f (void)
821 {
822 	//ZOID
823 	//if we are downloading, we don't change!  This so we don't suddenly stop downloading a map
824 	if (cls.download)
825 		return;
826 
827 	S_StopAllSounds ();
828 
829 	if (cls.state == ca_connected)
830 	{
831 		Com_Printf ("reconnecting (soft)...\n", LOG_CLIENT);
832 		//cls.state = ca_connected;
833 		MSG_WriteByte (clc_stringcmd);
834 		MSG_WriteString ("new");
835 		MSG_EndWriting (&cls.netchan.message);
836 		send_packet_now = true;
837 		return;
838 	}
839 
840 	if (cls.servername[0])
841 	{
842 		if (cls.state >= ca_connected) {
843 			Com_Printf ("disconnecting\n", LOG_CLIENT);
844 			strcpy (cls.lastservername, cls.servername);
845 			CL_Disconnect(false);
846 			cls.connect_time = cls.realtime - 1500;
847 		} else
848 			cls.connect_time = -99999; // fire immediately
849 
850 		cls.state = ca_connecting;
851 		//Com_Printf ("reconnecting (hard)...\n");
852 	}
853 
854 	if (cls.lastservername[0])
855 	{
856 		cls.connect_time = -99999;
857 		cls.state = ca_connecting;
858 		Com_Printf ("reconnecting (hard)...\n", LOG_CLIENT);
859 		strcpy (cls.servername, cls.lastservername);
860 	}
861 	else
862 	{
863 		Com_Printf ("No server to reconnect to.\n", LOG_CLIENT);
864 	}
865 }
866 
867 /*
868 =================
869 CL_CheckForResend
870 
871 Resend a connect message if the last one has timed out
872 =================
873 */
CL_CheckForResend(void)874 void CL_CheckForResend (void)
875 {
876 	netadr_t	adr;
877 
878 	// if the local server is running and we aren't
879 	// then connect
880 	if (cls.state == ca_disconnected && Com_ServerState() )
881 	{
882 		cls.state = ca_connecting;
883 		strcpy (cls.servername, "localhost");
884 		// we don't need a challenge on the localhost
885 		CL_SendConnectPacket (0);
886 		return;
887 //		cls.connect_time = -99999;	// CL_CheckForResend() will fire immediately
888 	}
889 
890 	// resend if we haven't gotten a reply yet
891 	if (cls.state != ca_connecting)
892 		return;
893 
894 	if (cls.realtime - cls.connect_time < 3000)
895 		return;
896 
897 	if (cl_proxy->string[0] && cls.proxyState != ps_active)
898 	{
899 		if (!NET_StringToAdr (cl_proxy->string, &cls.proxyAddr))
900 		{
901 			Com_Printf ("Bad proxy address: %s\n", LOG_CLIENT, cl_proxy->string);
902 			cls.state = ca_disconnected;
903 			cls.proxyState = ps_none;
904 			return;
905 		}
906 
907 		if (cls.proxyAddr.port == 0)
908 			cls.proxyAddr.port = ShortSwap (27999);
909 
910 		cls.proxyState = ps_pending;
911 	}
912 	else
913 	{
914 		if (!NET_StringToAdr (cls.servername, &adr))
915 		{
916 			Com_Printf ("Bad server address: %s\n", LOG_CLIENT, cls.servername);
917 			cls.state = ca_disconnected;
918 			cls.proxyState = ps_none;
919 			return;
920 		}
921 
922 		if (adr.port == 0)
923 			adr.port = ShortSwap (PORT_SERVER);
924 	}
925 
926 	cls.connect_time = cls.realtime;	// for retransmit requests
927 
928 	//_asm int 3;
929 	if (cls.proxyState == ps_pending)
930 	{
931 		Com_Printf ("Connecting to %s...\n", LOG_CLIENT, NET_AdrToString(&cls.proxyAddr));
932 		Netchan_OutOfBandProxyPrint (NS_CLIENT, &cls.proxyAddr, "proxygetchallenge\n");
933 	}
934 	else
935 	{
936 		Com_Printf ("Connecting to %s...\n", LOG_CLIENT, cls.servername);
937 		Netchan_OutOfBandPrint (NS_CLIENT, &adr, "getchallenge\n");
938 	}
939 }
940 
941 
942 /*
943 ================
944 CL_Connect_f
945 
946 ================
947 */
CL_Connect_f(void)948 void CL_Connect_f (void)
949 {
950 	char		*server, *p;
951 	netadr_t	adr;
952 
953 	if (Cmd_Argc() != 2)
954 	{
955 		Com_Printf ("usage: connect <server>\n", LOG_GENERAL);
956 		return;
957 	}
958 
959 	if (Com_ServerState ())
960 	{	// if running a local server, kill it and reissue
961 #ifndef NO_SERVER
962 		SV_Shutdown ("Server has shut down", false, false);
963 #endif
964 	}
965 	else
966 	{
967 		//we do this anyway below
968 		//CL_Disconnect (false);
969 	}
970 
971 	server = Cmd_Argv (1);
972 
973 	// quake2://protocol support
974 	if (!strncmp (server, "quake2://", 9))
975 		server += 9;
976 
977 	p = strchr (server, '/');
978 	if (p)
979 		p[0] = 0;
980 
981 	NET_Config (NET_CLIENT);		// allow remote
982 
983 	if (!NET_StringToAdr (server, &adr))
984 	{
985 		Com_Printf ("Bad server address: %s\n", LOG_CLIENT, server);
986 		return;
987 	}
988 
989 	if (adr.port == 0)
990 		adr.port = ShortSwap (PORT_SERVER);
991 
992 	CL_Disconnect (false);
993 
994 	//reset protocol attempt if we're connecting to a different server
995 	if (!NET_CompareAdr (&adr, &cls.netchan.remote_address))
996 	{
997 		Com_DPrintf ("Resetting protocol attempt since %s is not ", NET_AdrToString (&adr));
998 		Com_DPrintf ("%s.\n", NET_AdrToString (&cls.netchan.remote_address));
999 		cls.serverProtocol = 0;
1000 	}
1001 
1002 	cls.state = ca_connecting;
1003 	Q_strncpy (cls.servername, server, sizeof(cls.servername)-1);
1004 	strcpy (cls.lastservername, cls.servername);
1005 
1006 	cls.connect_time = -99999;	// CL_CheckForResend() will fire immediately
1007 }
1008 
1009 /*
1010 =====================
1011 CL_Rcon_f
1012 
1013   Send the rest of the command line over as
1014   an unconnected command.
1015 =====================
1016 */
CL_Rcon_f(void)1017 void CL_Rcon_f (void)
1018 {
1019 	char		message[1024];
1020 	netadr_t	to;
1021 
1022 	//r1: buffer check ffs!
1023 	if ((strlen(Cmd_Args()) + strlen(rcon_client_password->string) + 16) >= sizeof(message)) {
1024 		Com_Printf ("Length of password + command exceeds maximum allowed length.\n", LOG_CLIENT);
1025 		return;
1026 	}
1027 
1028 	*(int *)message = -1;
1029 	message[4] = 0;
1030 
1031 	NET_Config (NET_CLIENT);		// allow remote
1032 
1033 	strcat (message, "rcon ");
1034 
1035 	if (rcon_client_password->string[0])
1036 	{
1037 		strcat (message, rcon_client_password->string);
1038 		strcat (message, " ");
1039 	}
1040 
1041 	strcat (message, Cmd_Args());
1042 
1043 	/*for (i=1 ; i<Cmd_Argc() ; i++)
1044 	{
1045 		strcat (message, Cmd_Argv(i));
1046 		strcat (message, " ");
1047 	}*/
1048 
1049 
1050 	if (cls.state >= ca_connected)
1051 	{
1052 		to = cls.netchan.remote_address;
1053 	}
1054 	else
1055 	{
1056 		if (!strlen(rcon_address->string))
1057 		{
1058 			Com_Printf ("You must either be connected,\n"
1059 						"or set the 'rcon_address' cvar\n"
1060 						"to issue rcon commands\n", LOG_CLIENT);
1061 
1062 			return;
1063 		}
1064 		NET_StringToAdr (rcon_address->string, &to);
1065 		if (to.port == 0)
1066 			to.port = ShortSwap (PORT_SERVER);
1067 	}
1068 
1069 	//allow remote response
1070 	incoming_allowed[incoming_allowed_index & 15].remote = to;
1071 	incoming_allowed[incoming_allowed_index & 15].type = CL_RCON;
1072 	incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1073 	incoming_allowed_index++;
1074 
1075 	NET_SendPacket (NS_CLIENT, (int)strlen(message)+1, message, &to);
1076 }
1077 
CL_ServerStatus_f(void)1078 void CL_ServerStatus_f (void)
1079 {
1080 	netadr_t	adr;
1081 	int			i;
1082 	int			type;
1083 
1084 	i = 1;
1085 
1086 	if (!strcmp (Cmd_Argv(1), "/s"))
1087 	{
1088 		i++;
1089 		type = CL_SERVER_STATUS_FULL;
1090 	}
1091 	else if (!strcmp (Cmd_Argv(1), "/i"))
1092 	{
1093 		i++;
1094 		type = CL_SERVER_INFO;
1095 	}
1096 	else
1097 	{
1098 		type = CL_SERVER_STATUS;
1099 	}
1100 
1101 	NET_Config (NET_CLIENT);		// allow remote
1102 
1103 	if (i >= Cmd_Argc())
1104 	{
1105 		if (cls.state < ca_connected)
1106 		{
1107 			Com_Printf ("usage: serverstatus [/s|/i] [server]\n", LOG_GENERAL);
1108 			return;
1109 		}
1110 		adr = cls.netchan.remote_address;
1111 	}
1112 	else
1113 	{
1114 		if (!NET_StringToAdr (Cmd_Argv(i), &adr))
1115 		{
1116 			Com_Printf ("Bad address %s\n", LOG_CLIENT, Cmd_Argv(i));
1117 			return;
1118 		}
1119 	}
1120 
1121 	if (!adr.port)
1122 		adr.port = ShortSwap (PORT_SERVER);
1123 
1124 	if (type == CL_SERVER_INFO)
1125 		Netchan_OutOfBandPrint (NS_CLIENT, &adr, "info 34");
1126 	else
1127 		Netchan_OutOfBandPrint (NS_CLIENT, &adr, "status");
1128 
1129 	incoming_allowed[incoming_allowed_index & 15].remote = adr;
1130 	incoming_allowed[incoming_allowed_index & 15].type = type;
1131 	incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1132 	incoming_allowed_index++;
1133 }
1134 
1135 /*
1136 =====================
1137 CL_ClearState
1138 
1139 =====================
1140 */
CL_ClearState(void)1141 void CL_ClearState (void)
1142 {
1143 	cvar_t	*gameDirHack;
1144 
1145 	S_StopAllSounds ();
1146 	CL_ClearEffects ();
1147 	CL_ClearTEnts ();
1148 
1149 // wipe the entire cl structure
1150 	memset (&cl, 0, sizeof(cl));
1151 	memset (&cl_entities, 0, sizeof(cl_entities));
1152 
1153 	//r1 unprotect game
1154 	gameDirHack = Cvar_FindVar ("game");
1155 	gameDirHack->flags &= ~CVAR_NOSET;
1156 
1157 	//r1: local ents clear
1158 	Le_Reset ();
1159 
1160 	//r1: reset
1161 	cl.maxclients = MAX_CLIENTS;
1162 	SZ_Clear (&cls.netchan.message);
1163 
1164 	SZ_Init (&cl.demoBuff, cl.demoFrame, sizeof(cl.demoFrame));
1165 	cl.demoBuff.allowoverflow = true;
1166 
1167 	cl.settings[SVSET_FPS] = 10;
1168 }
1169 
1170 /*
1171 =====================
1172 CL_Disconnect
1173 
1174 Goes from a connected state to full screen console state
1175 Sends a disconnect message to the server
1176 This is also called on Com_Error, so it shouldn't cause any errors
1177 =====================
1178 */
CL_Disconnect(qboolean skipdisconnect)1179 void CL_Disconnect (qboolean skipdisconnect)
1180 {
1181 	byte	final[16];
1182 
1183 	if (cls.state == ca_disconnected)
1184 		return;
1185 
1186 	if (cl_timedemo->intvalue)
1187 	{
1188 		unsigned int	time;
1189 
1190 		time = Sys_Milliseconds () - cl.timedemo_start;
1191 		if (time > 0)
1192 			Com_Printf ("%i frames, %3.2f seconds: %3.1f fps\n", LOG_CLIENT, cl.timedemo_frames,
1193 			time/1000.0f, cl.timedemo_frames*1000.0f / time);
1194 	}
1195 
1196 	VectorClear (cl.refdef.blend);
1197 	re.CinematicSetPalette(NULL);
1198 
1199 	//r1: why?
1200 	//M_ForceMenuOff ();
1201 
1202 	cls.connect_time = 0;
1203 
1204 #ifdef CINEMATICS
1205 	SCR_StopCinematic ();
1206 #endif
1207 
1208 	if (cls.demorecording)
1209 		CL_EndRecording();
1210 		//CL_Stop_f ();
1211 
1212 	// send a disconnect message to the server
1213 	if (!skipdisconnect)
1214 	{
1215 		final[0] = clc_stringcmd;
1216 		strcpy ((char *)final+1, "disconnect");
1217 
1218 		Netchan_Transmit (&cls.netchan, 11, final);
1219 		Netchan_Transmit (&cls.netchan, 11, final);
1220 		Netchan_Transmit (&cls.netchan, 11, final);
1221 	}
1222 
1223 	CL_ClearState ();
1224 
1225 	// stop download
1226 	if (cls.download)
1227 	{
1228 		fclose(cls.download);
1229 		cls.download = NULL;
1230 	}
1231 
1232 #ifdef USE_CURL
1233 	CL_CancelHTTPDownloads (true);
1234 	cls.downloadReferer[0] = 0;
1235 #endif
1236 
1237 	cls.downloadname[0] = 0;
1238 	cls.downloadposition = 0;
1239 
1240 	cls.state = ca_disconnected;
1241 	cls.proxyState = ps_none;
1242 	cls.servername[0] = 0;
1243 
1244 	Cvar_ForceSet ("$mapname", "");
1245 	Cvar_ForceSet ("$game", "");
1246 	Cvar_ForceSet ("$serverip", "");
1247 
1248 	//r1: swap games if needed
1249 	Cvar_GetLatchedVars();
1250 
1251 	NET_SetProxy (NULL);
1252 	MSG_Clear();
1253 }
1254 
CL_Disconnect_f(void)1255 void CL_Disconnect_f (void)
1256 {
1257 	if (cls.state != ca_disconnected)
1258 	{
1259 		cls.serverProtocol = 0;
1260 		cls.key_dest = key_console;
1261 		Com_Error (ERR_HARD, "Disconnected from server");
1262 	}
1263 }
1264 
1265 //Spam repeated cmd for overflow checkings
1266 #ifdef _DEBUG
CL_Spam_f(void)1267 void CL_Spam_f (void)
1268 {
1269 	char	cmdBuff[1380] = {0};
1270 	int		count;
1271 	int		i;
1272 	char	*what;
1273 
1274 	count = atoi (Cmd_Argv(2));
1275 
1276 	if (!count)
1277 		return;
1278 
1279 	what = Cmd_Argv(1);
1280 
1281 	Q_strncpy (cmdBuff, Cmd_Argv(3), sizeof(cmdBuff)-1);
1282 	if (cmdBuff[0])
1283 		strcat (cmdBuff, " ");
1284 
1285 	if (count * strlen(what) >= sizeof(cmdBuff)-strlen(cmdBuff)-2)
1286 	{
1287 		Com_Printf ("Too long an expanded string.\n", LOG_CLIENT);
1288 		return;
1289 	}
1290 
1291 	for (i = 0; i < count; i++)
1292 		strcat (cmdBuff, what);
1293 
1294 	strcat (cmdBuff, "\n");
1295 
1296 	MSG_WriteByte (clc_stringcmd);
1297 	MSG_WriteString (cmdBuff);
1298 	MSG_EndWriting (&cls.netchan.message);
1299 
1300 	Com_Printf ("Wrote %d bytes stringcmd.\n", LOG_CLIENT, count * strlen(what));
1301 }
1302 #endif
1303 
1304 /*
1305 ====================
1306 CL_Packet_f
1307 
1308 packet <destination> <contents>
1309 
1310 Contents allows \n escape character
1311 ====================
1312 */
1313 #ifdef _DEBUG
CL_Packet_f(void)1314 void CL_Packet_f (void)
1315 {
1316 	char	send[2048];
1317 	int		i, l;
1318 	char	*in, *out;
1319 	netadr_t	adr;
1320 
1321 	if (Cmd_Argc() < 3)
1322 	{
1323 		Com_Printf ("packet <destination> <contents>\n", LOG_CLIENT);
1324 		return;
1325 	}
1326 
1327 	NET_Config (NET_CLIENT);		// allow remote
1328 
1329 	if (!NET_StringToAdr (Cmd_Argv(1), &adr))
1330 	{
1331 		Com_Printf ("Bad address\n", LOG_CLIENT);
1332 		return;
1333 	}
1334 
1335 	if (!adr.port)
1336 		adr.port = ShortSwap (PORT_SERVER);
1337 
1338 	in = Cmd_Args2(2);
1339 	out = send+4;
1340 
1341 	*(int *)send = -1;
1342 
1343 	l = (int)strlen (in);
1344 	for (i=0 ; i<l ; i++)
1345 	{
1346 		if (out - send >= sizeof(send)-1)
1347 			break;
1348 
1349 		if (in[i] == '\\' && in[i+1] == 'n')
1350 		{
1351 			*out++ = '\n';
1352 			i++;
1353 		}
1354 		else
1355 			*out++ = in[i];
1356 	}
1357 	*out = 0;
1358 
1359 	incoming_allowed[incoming_allowed_index & 15].remote = adr;
1360 	incoming_allowed[incoming_allowed_index & 15].type = CL_UNDEF;
1361 	incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1362 	incoming_allowed_index++;
1363 
1364 	NET_SendPacket (NS_CLIENT, (int)(out-send), send, &adr);
1365 }
1366 #endif
1367 
1368 /*
1369 =================
1370 CL_Changing_f
1371 
1372 Just sent as a hint to the client that they should
1373 drop to full console
1374 =================
1375 */
CL_Changing_f(void)1376 void CL_Changing_f (void)
1377 {
1378 	char	*cmd;
1379 
1380 	//ZOID
1381 	//if we are downloading, we don't change!  This so we don't suddenly stop downloading a map
1382 	if (cls.download)
1383 		return;
1384 
1385 	if (cls.demofile)
1386 		CL_EndRecording();
1387 
1388 	//force screen update in case user has screenshot etc they want doing
1389 	SCR_UpdateScreen ();
1390 
1391 	cmd = Cmd_MacroExpandString("$cl_endmapcmd");
1392 	if (cmd)
1393 	{
1394 		if (cmd[0])
1395 		{
1396 			char	*p;
1397 			//note, can't use Cmd_ExecuteString as it doesn't handle ;
1398 			//but we must otherwise we don't run immediately!
1399 			p = cmd;
1400 
1401 			for (;;)
1402 			{
1403 				p = strchr (p, ';');
1404 				if (p)
1405 				{
1406 					p[0] = 0;
1407 					Cmd_ExecuteString (cmd);
1408 					p = cmd = p + 1;
1409 					if (!cmd[0])
1410 						break;
1411 				}
1412 				else
1413 				{
1414 					Cmd_ExecuteString (cmd);
1415 					break;
1416 				}
1417 			}
1418 		}
1419 	}
1420 	else
1421 		Com_Printf ("WARNING: Error expanding $cl_endmapcmd, ignored.\n", LOG_CLIENT|LOG_WARNING);
1422 
1423 	//r1: prevent manual issuing crashing game
1424 	if (cls.state < ca_connected)
1425 		return;
1426 
1427 	//r1: stop loading models right now
1428 	deferred_model_index = MAX_MODELS;
1429 
1430 	SCR_BeginLoadingPlaque ();
1431 	cls.state = ca_connected;	// not active anymore, but not disconnected
1432 	Com_Printf ("\nChanging map...\n", LOG_CLIENT);
1433 
1434 	//shut up
1435 	noFrameFromServerPacket = 0;
1436 }
1437 
1438 
1439 /*
1440 =================
1441 CL_ParseStatusMessage
1442 
1443 Handle a reply from a ping
1444 =================
1445 */
CL_ParseStatusMessage(void)1446 void CL_ParseStatusMessage (void)
1447 {
1448 	char	*s;
1449 
1450 	s = MSG_ReadString (&net_message);
1451 
1452 	Com_Printf ("%s\n", LOG_CLIENT, s);
1453 	M_AddToServerList (net_from, s);
1454 }
1455 
1456 //FIXME: add this someday
1457 /*void CL_GetServers_f (void)
1458 {
1459 	netadr_t		adr;
1460 
1461 	if (Cmd_Argc() < 2)
1462 	{
1463 		Com_Printf ("Usage: getservers <master>\n", LOG_GENERAL);
1464 		return;
1465 	}
1466 
1467 	NET_StringToAdr (Cmd_Argv(1), &adr);
1468 
1469 	Netchan_OutOfBandPrint (NS_CLIENT, &adr, "getservers");
1470 
1471 	//allow remote response
1472 	incoming_allowed[incoming_allowed_index & 15].remote = to;
1473 	incoming_allowed[incoming_allowed_index & 15].type = CL_MASTER_QUERY;
1474 	incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1475 	incoming_allowed_index++;
1476 }*/
1477 
1478 /*
1479 =================
1480 CL_PingServers_f
1481 =================
1482 */
CL_PingServers_f(void)1483 void CL_PingServers_f (void)
1484 {
1485 	int				i;
1486 	netadr_t		adr;
1487 	char			name[16];
1488 	const char		*adrstring;
1489 	//cvar_t		*noudp;
1490 	//cvar_t		*noipx;
1491 
1492 	NET_Config (NET_CLIENT);		// allow remote
1493 
1494 	// send a broadcast packet
1495 	Com_Printf ("pinging broadcast...\n", LOG_CLIENT|LOG_NOTICE);
1496 
1497 	adr.type = NA_BROADCAST;
1498 	adr.port = ShortSwap(PORT_SERVER);
1499 
1500 	//r1: only ping original, r1q2 servers respond to either, but 3.20 server would
1501 	//reply with errors on receiving enhanced info
1502 	Netchan_OutOfBandPrint (NS_CLIENT, &adr, "info 34\n");
1503 
1504 	//also ping local server
1505 	adr.type = NA_IP;;
1506 	*(int *)&adr.ip = 0x100007F;
1507 	adr.port = ShortSwap(PORT_SERVER);
1508 	Netchan_OutOfBandPrint (NS_CLIENT, &adr, "info 34\n");
1509 
1510 	// send a packet to each address book entry
1511 	for (i=0 ; i < 32; i++)
1512 	{
1513 		Com_sprintf (name, sizeof(name), "adr%i", i);
1514 		adrstring = Cvar_VariableString (name);
1515 		if (!adrstring || !adrstring[0])
1516 			continue;
1517 
1518 		Com_Printf ("pinging %s...\n", LOG_CLIENT|LOG_NOTICE, adrstring);
1519 		if (!NET_StringToAdr (adrstring, &adr))
1520 		{
1521 			Com_Printf ("Bad address: %s\n", LOG_CLIENT, adrstring);
1522 			continue;
1523 		}
1524 		if (!adr.port)
1525 			adr.port = ShortSwap(PORT_SERVER);
1526 		//Netchan_OutOfBandPrint (NS_CLIENT, adr, va("info %i", PROTOCOL_R1Q2));
1527 		Netchan_OutOfBandPrint (NS_CLIENT, &adr, "info %i\n", PROTOCOL_ORIGINAL);
1528 	}
1529 }
1530 
1531 
1532 /*
1533 =================
1534 CL_Skins_f
1535 
1536 Load or download any custom player skins and models
1537 =================
1538 */
1539 
CL_Skins_f(void)1540 void CL_Skins_f (void)
1541 {
1542 	int		i;
1543 
1544 	for (i=0 ; i < MAX_CLIENTS ; i++)
1545 	{
1546 		if (!cl.configstrings[CS_PLAYERSKINS+i][0])
1547 			continue;
1548 		//if (developer->intvalue)
1549 		Com_Printf ("client %i: %s\n", LOG_CLIENT, i, cl.configstrings[CS_PLAYERSKINS+i]);
1550 		//SCR_UpdateScreen ();
1551 		//Sys_SendKeyEvents ();	// pump message loop
1552 		CL_ParseClientinfo (i);
1553 	}
1554 	//Com_Printf ("Precached all skins.\n", LOG_CLIENT);
1555 }
1556 
CL_ProxyPacket(void)1557 void CL_ProxyPacket (void)
1558 {
1559 	char	*s;
1560 	char	*c;
1561 
1562 	if (cls.proxyState != ps_pending)
1563 		return;
1564 
1565 	MSG_BeginReading (&net_message);
1566 	MSG_ReadLong (&net_message);	// skip the -2
1567 
1568 	s = MSG_ReadStringLine (&net_message);
1569 
1570 	Cmd_TokenizeString (s, false);
1571 
1572 	c = Cmd_Argv(0);
1573 
1574 	if (!strcmp (c, "proxychallenge"))
1575 	{
1576 		netadr_t	remote_addr;
1577 		unsigned long challenge = strtoul(Cmd_Argv(1), NULL, 10);
1578 
1579 		if (!NET_StringToAdr (cls.servername, &remote_addr))
1580 			return;
1581 
1582 		if (remote_addr.port == 0)
1583 			remote_addr.port = ShortSwap (PORT_SERVER);
1584 
1585 		Netchan_OutOfBandProxyPrint (NS_CLIENT, &cls.proxyAddr, "proxyconnect %lu %s\n", challenge, NET_AdrToString (&remote_addr));
1586 	}
1587 	else if (!strcmp (c, "proxyactive"))
1588 	{
1589 		int	port;
1590 
1591 		port = atoi (Cmd_Argv(1));
1592 
1593 		if (port >= 1024)
1594 			net_from.port = ShortSwap(port);
1595 
1596 		NET_SetProxy (&net_from);
1597 		cls.proxyAddr = net_from;
1598 		cls.proxyState = ps_active;
1599 		cls.connect_time = -99999; // fire immediately
1600 	}
1601 }
1602 
1603 /*
1604 =================
1605 CL_ConnectionlessPacket
1606 
1607 Responses to broadcasts, etc
1608 =================
1609 */
CL_ConnectionlessPacket(void)1610 void CL_ConnectionlessPacket (void)
1611 {
1612 	int		type;
1613 	int		i;
1614 
1615 	char	*s;
1616 	char	*c;
1617 
1618 	netadr_t	remote_addr;
1619 	netadr_t	*remote;
1620 
1621 	MSG_BeginReading (&net_message);
1622 	MSG_ReadLong (&net_message);	// skip the -1
1623 
1624 	s = MSG_ReadStringLine (&net_message);
1625 
1626 	NET_StringToAdr (cls.servername, &remote_addr);
1627 
1628 	remote = &remote_addr;
1629 
1630 	Cmd_TokenizeString (s, false);
1631 
1632 	c = Cmd_Argv(0);
1633 
1634 	type = CL_UNDEF;
1635 
1636 	//r1: server responding to a status broadcast (ignores security check due
1637 	//to broadcasts responding)
1638 	if (!strcmp(c, "info"))
1639 	{
1640 		if (cls.state == ca_disconnected || cls.key_dest != key_game)
1641 			CL_ParseStatusMessage ();
1642 		return;
1643 	}
1644 	else if (!strcmp (c, "passive_connect"))
1645 	{
1646 		if (!cls.passivemode)
1647 		{
1648 			Com_DPrintf ("Illegal %s from %s.  Ignored.\n", c, NET_AdrToString (&net_from));
1649 		}
1650 		else
1651 		{
1652 			Com_Printf ("Received %s from %s -- beginning connection.\n", LOG_CLIENT, c, NET_AdrToString (&net_from));
1653 			strcpy (cls.servername, NET_AdrToString (&net_from));
1654 			cls.state = ca_connecting;
1655 			cls.connect_time = -99999;
1656 			cls.passivemode = false;
1657 		}
1658 		return;
1659 	}
1660 
1661 	//r1: security check. (only allow from current connected server
1662 	//and last destinations client sent a packet to)
1663 	for (i = 0; i < sizeof(incoming_allowed) / sizeof(incoming_allowed[0]); i++)
1664 	{
1665 		if (incoming_allowed[i].time > cls.realtime && NET_CompareBaseAdr (&net_from, &incoming_allowed[i].remote))
1666 		{
1667 			type = incoming_allowed[i].type;
1668 			//invalidate
1669 			incoming_allowed[i].time = 0;
1670 			goto safe;
1671 		}
1672 	}
1673 
1674 	if (!NET_CompareBaseAdr (&net_from, remote) && !NET_CompareBaseAdr (&net_from, &cls.proxyAddr))
1675 	{
1676 		Com_DPrintf ("Illegal %s from %s.  Ignored.\n", c, NET_AdrToString (&net_from));
1677 		return;
1678 	}
1679 
1680 safe:
1681 
1682 	if (type == CL_UNDEF)
1683 	{
1684 		//r1: spamfix: don't echo packet type mid-game
1685 		if (cls.state < ca_active)
1686 			Com_Printf ("%s: %s\n", LOG_CLIENT, NET_AdrToString (&net_from), c);
1687 	}
1688 
1689 	// server connection
1690 	if (!strcmp(c, "client_connect"))
1691 	{
1692 #ifdef ANTICHEAT
1693 		qboolean	try_to_use_anticheat;
1694 #endif
1695 		int			i;
1696 		char		*buff, *p;
1697 
1698 		if (cls.state == ca_connected)
1699 		{
1700 			Com_DPrintf ("Dup connect received.  Ignored.\n");
1701 			return;
1702 
1703 			//r1: note, next 2 should be redundant with the above global check.
1704 		} else if (cls.state == ca_disconnected) {
1705 			//FIXME: this should never happen (disconnecting nukes remote, no remote
1706 			//= no packet)
1707 			Com_DPrintf ("Received connect when disconnected.  Ignored.\n");
1708 			return;
1709 		} else if (cls.state == ca_active) {
1710 			Com_DPrintf ("Illegal connect when already connected !! (q2msgs?).  Ignored.\n");
1711 			return;
1712 		}
1713 
1714 		Com_DPrintf ("client_connect: new\n");
1715 
1716 		Netchan_Setup (NS_CLIENT, &cls.netchan, &net_from, cls.serverProtocol, cls.quakePort, 0);
1717 
1718 		buff = NET_AdrToString(&cls.netchan.remote_address);
1719 
1720 #ifdef ANTICHEAT
1721 		try_to_use_anticheat = false;
1722 #endif
1723 
1724 		for (i = 1; i < Cmd_Argc(); i++)
1725 		{
1726 			p = Cmd_Argv(i);
1727 			if (!strncmp (p, "dlserver=", 9))
1728 			{
1729 #ifdef USE_CURL
1730 				p += 9;
1731 				Com_sprintf (cls.downloadReferer, sizeof(cls.downloadReferer), "quake2://%s", buff);
1732 				CL_SetHTTPServer (p);
1733 				if (cls.downloadServer[0])
1734 					Com_Printf ("HTTP downloading enabled, URL: %s\n", LOG_CLIENT|LOG_NOTICE, cls.downloadServer);
1735 #else
1736 				Com_Printf ("HTTP downloading supported by server but this client was built without USE_CURL, bad luck.\n", LOG_CLIENT);
1737 #endif
1738 			}
1739 #ifdef ANTICHEAT
1740 			else if (!strncmp (p, "ac=", 3))
1741 			{
1742 				p+= 3;
1743 				if (!p[0])
1744 					continue;
1745 				if (atoi (p))
1746 					try_to_use_anticheat = true;
1747 			}
1748 #endif
1749 		}
1750 
1751 		//note, not inside the loop as we could potentially clobber cmd_argc/cmd_argv
1752 #ifdef ANTICHEAT
1753 		if (try_to_use_anticheat)
1754 		{
1755 			MSG_WriteByte (clc_nop);
1756 			MSG_EndWriting (&cls.netchan.message);
1757 			Netchan_Transmit (&cls.netchan, 0, NULL);
1758 			S_StopAllSounds ();
1759 			con.ormask = 128;
1760 			Com_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n", LOG_CLIENT);
1761 			Com_Printf ("Loading anticheat, this may take a few moments...\n", LOG_GENERAL);
1762 			Com_Printf("\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n", LOG_CLIENT);
1763 			con.ormask = 0;
1764 			SCR_UpdateScreen ();
1765 			SCR_UpdateScreen ();
1766 			SCR_UpdateScreen ();
1767 			if (!Sys_GetAntiCheatAPI ())
1768 				Com_Printf ("        ERROR: anticheat.dll failed to load\n"
1769 							"Either the file is missing, or something is preventing\n"
1770 							"it from loading or connecting to the anticheat server.\n"
1771 							"This is commonly caused by over-aggressive anti-virus\n"
1772 							"software. Try disabling any anti-virus or other security\n"
1773 							"software or add an exception for anticheat.dll. Make sure\n"
1774 							"your system date/time is set correctly as this can prevent\n"
1775 							"anticheat from working.\n"
1776 							"\n"
1777 							"Trying to connect without anticheat support.\n", LOG_GENERAL);
1778 		}
1779 #endif
1780 		p = strchr (buff, ':');
1781 		if (p)
1782 			p[0] = 0;
1783 		Cvar_ForceSet ("$serverip", buff);
1784 
1785 		MSG_WriteByte (clc_stringcmd);
1786 		MSG_WriteString ("new");
1787 		MSG_EndWriting (&cls.netchan.message);
1788 
1789 		cls.key_dest = key_game;
1790 
1791 		//CL_FixCvarCheats();
1792 		send_packet_now = true;
1793 		cls.state = ca_connected;
1794 		return;
1795 	}
1796 
1797 	// remote command from gui front end
1798 	// r1: removed, this is dangerous to leave in with new NET_IsLocalAddress code.
1799 	/*if (!strcmp(c, "cmd"))
1800 	{
1801 		if (!NET_IsLocalAddress(&net_from))
1802 		{
1803 			Com_DPrintf ("Command packet from remote host.  Ignored.\n");
1804 			return;
1805 		}
1806 		Sys_AppActivate ();
1807 		s = MSG_ReadString (&net_message);
1808 		Cbuf_AddText (s);
1809 		Cbuf_AddText ("\n");
1810 		return;
1811 	}*/
1812 
1813 	// print command from somewhere
1814 	if (!strcmp(c, "print"))
1815 	{
1816 		s = MSG_ReadString (&net_message);
1817 
1818 		switch (type)
1819 		{
1820 			case CL_RCON:
1821 				//rcon can come in multiple packets
1822 				incoming_allowed[incoming_allowed_index & 15].remote = net_from;
1823 				incoming_allowed[incoming_allowed_index & 15].type = CL_RCON;
1824 				incoming_allowed[incoming_allowed_index & 15].time = cls.realtime + 2500;
1825 				incoming_allowed_index++;
1826 
1827 				//intentional fallthrough
1828 			case CL_SERVER_INFO:
1829 				Com_Printf ("%s", LOG_CLIENT, s);
1830 				break;
1831 			case CL_SERVER_STATUS:
1832 			case CL_SERVER_STATUS_FULL:
1833 				{
1834 					char	*p;
1835 					char	*player_name;
1836 					char	*player_score;
1837 					char	*player_ping;
1838 
1839 					//skip the serverinfo
1840 					if (type == CL_SERVER_STATUS)
1841 					{
1842 						p = strstr (s, "\n");
1843 						if (!p)
1844 							return;
1845 						else
1846 							p++;
1847 					}
1848 					else
1849 					{
1850 						int		flip;
1851 
1852 						Com_Printf ("Server Info\n"
1853 									"-----------\n", LOG_CLIENT);
1854 
1855 						flip = 0;
1856 						p = s + 1;
1857 
1858 						//make it more readable
1859 						while (p[0])
1860 						{
1861 							if (p[0] == '\\')
1862 							{
1863 								if (flip)
1864 									p[0] = '\n';
1865 								else
1866 									p[0] = '=';
1867 								flip ^= 1;
1868 							}
1869 							else if (p[0] == '\n')
1870 							{
1871 								while (p[0] && p[0] == '\n')
1872 								{
1873 									p[0] = 0;
1874 									p++;
1875 								}
1876 								break;
1877 							}
1878 							p++;
1879 						}
1880 						Com_Printf ("%s\n", LOG_CLIENT, s+1);
1881 					}
1882 
1883 					Com_Printf ("Name            Score Ping\n"
1884 								"--------------- ----- ----\n", LOG_CLIENT);
1885 
1886 		/*
1887 		0 0 "OCLD"
1888 		86 86 "a vehicle"
1889 		81 28 "1ST-Timer"
1890 		27 51 "[DSH]Fella"
1891 		86 24 "[MCB]Jonny"
1892 		*/
1893 					//icky icky parser
1894 					while (p[0])
1895 					{
1896 						if (isdigit (p[0]))
1897 						{
1898 							player_score = p;
1899 							p = strchr (player_score, ' ');
1900 							if (p)
1901 							{
1902 								p[0] = 0;
1903 								p++;
1904 								if (isdigit (p[0]))
1905 								{
1906 									player_ping = p;
1907 									p = strchr (player_ping, ' ');
1908 									if (p)
1909 									{
1910 										p[0] = 0;
1911 										p++;
1912 										if (p[0] == '"')
1913 										{
1914 											player_name = p + 1;
1915 											p = strchr (player_name, '"');
1916 											if (p)
1917 											{
1918 												p[0] = 0;
1919 												p++;
1920 												if (p[0] == '\n')
1921 												{
1922 													Com_Printf ("%-15s %5s %4s\n", LOG_CLIENT, player_name, player_score, player_ping);
1923 													p++;
1924 												} else return;
1925 											} else return;
1926 										} else return;
1927 									} else return;
1928 								} else return;
1929 							} else return;
1930 						} else return;
1931 					}
1932 				}
1933 				break;
1934 			default:
1935 				//BIG HACK to allow new client on old server!
1936 
1937 				//r1: spoofed spam fix, only allow print during connection process, other legit cases should be handled above
1938 				if (cls.state == ca_connecting || cls.state == ca_connected)
1939 					Com_Printf ("%s", LOG_CLIENT, s);
1940 
1941 				//r1: security fix: only do protocol fallback when connecting.
1942 				if (cls.state == ca_connecting)
1943 				{
1944 					if (!strstr (s, "full") &&
1945 						!strstr (s, "locked") &&
1946 						!strncmp (s, "Server is ", 10) &&
1947 						cls.serverProtocol != PROTOCOL_ORIGINAL)
1948 					{
1949 						Com_Printf ("Retrying with protocol %d.\n", LOG_CLIENT, PROTOCOL_ORIGINAL);
1950 						cls.serverProtocol = PROTOCOL_ORIGINAL;
1951 						//force immediate retry
1952 						cls.connect_time = -99999;
1953 
1954 					}
1955 				}
1956 				break;
1957 		}
1958 		return;
1959 	}
1960 
1961 	// ping from somewhere
1962 
1963 	//r1: removed, could be used for flooding modemers
1964 	//r1: tested, can even lag out a cable connect !
1965 
1966 	/*if (!strcmp(c, "ping"))
1967 	{
1968 		Netchan_OutOfBandPrint (NS_CLIENT, net_from, "ack");
1969 		return;
1970 	}*/
1971 
1972 	// challenge from the server we are connecting to
1973 	if (!strcmp(c, "challenge"))
1974 	{
1975 		int		protocol = PROTOCOL_ORIGINAL;
1976 		int		i;
1977 		char	*p;
1978 
1979 		if (cls.state != ca_connecting)
1980 		{
1981 			Com_DPrintf ("Illegal challenge from server whilst already connected, ignored.\n");
1982 			return;
1983 		}
1984 
1985 		cls.challenge = atoi(Cmd_Argv(1));
1986 
1987 		// available protocol versions now in challenge, until few months we still default to brute.
1988 		for (i = 2; i < Cmd_Argc(); i++)
1989 		{
1990 			p = Cmd_Argv(i);
1991 			if (!strncmp (p, "p=", 2))
1992 			{
1993 				p += 2;
1994 				if (!p[0])
1995 					continue;
1996 				for (;;)
1997 				{
1998 					i = atoi (p);
1999 					if (i == PROTOCOL_R1Q2)
2000 						protocol = i;
2001 					p = strchr(p, ',');
2002 					if (!p)
2003 						break;
2004 					p++;
2005 					if (!p[0])
2006 						break;
2007 				}
2008 				break;
2009 			}
2010 		}
2011 
2012 		//r1: reset the timer so we don't send dup. getchallenges
2013 		cls.connect_time = cls.realtime;
2014 		CL_SendConnectPacket (protocol);
2015 		return;
2016 	}
2017 
2018 	// echo request from server
2019 	//r1: removed, no real purpose and possible attack vector
2020 	/*if (!strcmp(c, "echo"))
2021 	{
2022 		Netchan_OutOfBandPrint (NS_CLIENT, &net_from, "%s", Cmd_Argv(1) );
2023 		return;
2024 	}*/
2025 
2026 	Com_DPrintf ("Unknown connectionless packet command %s\n", c);
2027 }
2028 
2029 /*
2030 =================
2031 CL_ReadPackets
2032 =================
2033 */
CL_ReadPackets(void)2034 void CL_ReadPackets (void)
2035 {
2036 	int i;
2037 
2038 #ifdef _DEBUG
2039 	memset (net_message_buffer, 31, sizeof(net_message_buffer));
2040 #endif
2041 
2042 	while ((i = (NET_GetPacket (NS_CLIENT, &net_from, &net_message))))
2043 	{
2044 		//
2045 		// remote command packet
2046 		//
2047 		if (*(int *)net_message_buffer == -1)
2048 		{
2049 			if (i == -1 && cls.key_dest != key_game)
2050 				Com_Printf ("Port unreachable from %s\n", LOG_CLIENT|LOG_NOTICE, NET_AdrToString (&net_from));
2051 			else
2052 				CL_ConnectionlessPacket ();
2053 			continue;
2054 		}
2055 		else if (*(int *)net_message_buffer == -2)
2056 		{
2057 			CL_ProxyPacket ();
2058 			continue;
2059 		}
2060 
2061 		if (cls.state <= ca_connecting)
2062 			continue;		// dump it if not connected
2063 
2064 		if (net_message.cursize < 8)
2065 		{
2066 			//r1: delegated to DPrintf (someone could spam your console with crap otherwise)
2067 			Com_DPrintf ("%s: Runt packet\n",NET_AdrToString(&net_from));
2068 			continue;
2069 		}
2070 
2071 		//
2072 		// packet from server
2073 		//
2074 		if (!NET_CompareAdr (&net_from, &cls.netchan.remote_address))
2075 		{
2076 			Com_DPrintf ("%s:sequenced packet without connection\n"
2077 				,NET_AdrToString(&net_from));
2078 			continue;
2079 		}
2080 
2081 		if (i == -1)
2082 			Com_Error (ERR_HARD, "Connection reset by peer.");
2083 
2084 		if (!Netchan_Process(&cls.netchan, &net_message))
2085 			continue;		// wasn't accepted for some reason
2086 
2087 		if (cls.netchan.got_reliable)
2088 		{
2089 			if (cls.state == ca_connected)
2090 				send_packet_now = true;
2091 			else if (cl_instantack->intvalue)
2092 				Netchan_Transmit (&cls.netchan, 0, NULL);
2093 
2094 			if (cl_shownet->intvalue == 1)
2095 				Com_Printf ("r", LOG_CLIENT);
2096 		}
2097 
2098 		if (CL_ParseServerMessage ())
2099 		{
2100 			CL_AddNetgraph ();
2101 
2102 			if (scr_sizegraph->intvalue)
2103 				CL_AddSizegraph ();
2104 		}
2105 
2106 		//
2107 		// we don't know if it is ok to save a demo message until
2108 		// after we have parsed the frame
2109 		//
2110 		if (cls.demorecording && !cls.demowaiting && cls.serverProtocol == PROTOCOL_ORIGINAL)
2111 			CL_WriteDemoMessageFull ();
2112 	}
2113 
2114 	//
2115 	// check timeout
2116 	//
2117 	if (cls.state >= ca_connected
2118 	 && cls.realtime - cls.netchan.last_received > cl_timeout->intvalue*1000)
2119 	{
2120 		if (++cl.timeoutcount > 5)	// timeoutcount saves debugger
2121 		{
2122 			Com_Printf ("\nServer connection timed out.\n", LOG_CLIENT);
2123 #ifdef _DEBUG
2124 			CL_Reconnect_f ();
2125 #else
2126 			CL_Disconnect (false);
2127 #endif
2128 			return;
2129 		}
2130 	}
2131 	else
2132 		cl.timeoutcount = 0;
2133 
2134 }
2135 
2136 
2137 //=============================================================================
2138 
2139 /*
2140 ==============
2141 CL_FixUpGender_f
2142 ==============
2143 */
2144 
2145 //r1: FIXME: this seems.. wrong. yet another excuse for a client dll.
CL_FixUpGender(void)2146 void CL_FixUpGender(void)
2147 {
2148 	char *p;
2149 	char sk[80];
2150 
2151 	if (gender_auto->intvalue) {
2152 
2153 		if (gender->modified) {
2154 			// was set directly, don't override the user
2155 			gender->modified = false;
2156 			return;
2157 		}
2158 
2159 		Q_strncpy(sk, skin->string, sizeof(sk) - 1);
2160 		if ((p = strchr(sk, '/')) != NULL)
2161 			p[0] = 0;
2162 		if (Q_stricmp(sk, "male") == 0 || Q_stricmp(sk, "cyborg") == 0)
2163 			Cvar_Set ("gender", "male");
2164 		else if (Q_stricmp(sk, "female") == 0 || Q_stricmp(sk, "crackhor") == 0)
2165 			Cvar_Set ("gender", "female");
2166 		else
2167 			Cvar_Set ("gender", "none");
2168 		gender->modified = false;
2169 	}
2170 }
2171 
2172 /*
2173 ==============
2174 CL_Userinfo_f
2175 ==============
2176 */
CL_Userinfo_f(void)2177 void CL_Userinfo_f (void)
2178 {
2179 	Com_Printf ("User info settings:\n", LOG_CLIENT);
2180 	Info_Print (Cvar_Userinfo());
2181 }
2182 
2183 /*
2184 =================
2185 CL_Snd_Restart_f
2186 
2187 Restart the sound subsystem so it can pick up
2188 new parameters and flush all sounds
2189 =================
2190 */
CL_Snd_Restart_f(void)2191 void CL_Snd_Restart_f (void)
2192 {
2193 	S_Shutdown ();
2194 
2195 	if (Cmd_Argc() == 1)
2196 		S_Init (-1);
2197 	else
2198 		S_Init (atoi(Cmd_Argv(1)));
2199 
2200 	memset (cl.sound_precache, 0, sizeof(cl.sound_precache));
2201 	CL_RegisterSounds ();
2202 }
2203 
2204 int precache_check; // for autodownload of precache items
2205 int precache_spawncount;
2206 int precache_tex;
2207 int precache_model_skin;
2208 uint32 precache_start_time;
2209 
2210 static byte *precache_model; // used for skin checking in alias models
2211 
2212 //#define	PLAYER_MULT	5
2213 
2214 // ENV_CNT is map load, ENV_CNT+1 is first env map
2215 #define ENV_CNT (CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
2216 #define TEXTURE_CNT (ENV_CNT+13)
2217 
2218 static const char *env_suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"};
2219 
2220 typedef struct cl_location_s
2221 {
2222 	struct cl_location_s	*next;
2223 	char					*name;
2224 	vec3_t					location;
2225 } cl_location_t;
2226 
2227 static cl_location_t	cl_locations;
2228 
CL_Loc_Init(void)2229 void CL_Loc_Init (void)
2230 {
2231 	memset (&cl_locations, 0, sizeof(cl_locations));
2232 }
2233 
CL_FreeLocs(void)2234 void CL_FreeLocs (void)
2235 {
2236 	cl_location_t	*loc = &cl_locations,
2237 					*old = NULL;
2238 
2239 	while (loc->next)
2240 	{
2241 		loc = loc->next;
2242 		if (old)
2243 		{
2244 			Z_Free (old->name);
2245 			Z_Free (old);
2246 		}
2247 		old = loc;
2248 	}
2249 
2250 	if (old)
2251 	{
2252 		Z_Free (old->name);
2253 		Z_Free (old);
2254 	}
2255 
2256 	cl_locations.next = NULL;
2257 }
2258 
2259 //you wanted locs, you got em... dear god this is terrible code :)
CL_LoadLoc(const char * filename)2260 qboolean CL_LoadLoc (const char *filename)
2261 {
2262 	char	*locBuffer;
2263 	cl_location_t	*loc = &cl_locations;
2264 	char *x, *y, *z, *name, *line;
2265 	int	linenum;
2266 	int len;
2267 	FILE	*handle;
2268 	qboolean	closeFile;
2269 
2270 	CL_FreeLocs ();
2271 
2272 	len = FS_LoadFile ((char *)filename, NULL);
2273 
2274 	if (len == -1)
2275 	{
2276 		Com_DPrintf ("CL_LoadLoc: %s not found\n", filename);
2277 		return false;
2278 	}
2279 
2280 	FS_FOpenFile (filename, &handle, HANDLE_OPEN, &closeFile);
2281 	if (!handle)
2282 	{
2283 		Com_Printf ("CL_LoadLoc: couldn't open %s\n", LOG_CLIENT|LOG_WARNING, filename);
2284 		return false;
2285 	}
2286 
2287 	locBuffer = Z_TagMalloc (len+2, TAGMALLOC_CLIENT_LOC);
2288 	//locBuffer = alloca (len+2);
2289 	FS_Read (locBuffer, len, handle);
2290 
2291 	if (closeFile)
2292 		FS_FCloseFile (handle);
2293 
2294 	//terminate if no EOL
2295 	locBuffer[len-1] = '\n';
2296 	locBuffer[len] = 0;
2297 
2298 	linenum = 0;
2299 
2300 	line = locBuffer;
2301 
2302 	for (;;)
2303 	{
2304 		//eof
2305 		if (line - locBuffer >= len)
2306 			break;
2307 
2308 		//skip whitespace
2309 		while (*line && (*line == ' ' || *line == '\t' || *line == '\r' || *line == '\n'))
2310 		{
2311 			line++;
2312 			if (line - locBuffer >= len)
2313 				break;
2314 		}
2315 
2316 		//eof now?
2317 		if (line - locBuffer >= len)
2318 			break;
2319 
2320 		linenum++;
2321 		x = line;
2322 
2323 		name = y = z = NULL;
2324 
2325 		while (*line)
2326 		{
2327 			if (*line == ' ' || *line == '\t') {
2328 				*line++ = '\0';
2329 				y = line;
2330 				break;
2331 			}
2332 			line++;
2333 			if (line - locBuffer >= len)
2334 				break;
2335 		}
2336 
2337 		while (*line)
2338 		{
2339 			if (*line == ' ' || *line == '\t') {
2340 				*line++ = '\0';
2341 				z = line;
2342 				break;
2343 			}
2344 			line++;
2345 			if (line - locBuffer >= len)
2346 				break;
2347 		}
2348 
2349 		if (line - locBuffer >= len)
2350 			break;
2351 
2352 		while (*line)
2353 		{
2354 			if (*line == ' ' || *line == '\t')
2355 			{
2356 				*line++ = '\0';
2357 				name = line;
2358 				break;
2359 			}
2360 			line++;
2361 			if (line - locBuffer >= len)
2362 				break;
2363 		}
2364 
2365 		if (line - locBuffer >= len)
2366 			break;
2367 
2368 		while (*line)
2369 		{
2370 			if (*line == '\n' || *line == '\r') {
2371 				*line = '\0';
2372 				break;
2373 			}
2374 			line++;
2375 			if (line - locBuffer >= len)
2376 				break;
2377 		}
2378 
2379 		if (!*x || !y || !*y || !z || !*z || !name || !*name)
2380 		{
2381 			Com_Printf ("CL_LoadLoc: Parse error on line %d of '%s'.\n", LOG_CLIENT, linenum, filename);
2382 			break;
2383 		}
2384 
2385 		loc->next = Z_TagMalloc (sizeof(cl_location_t), TAGMALLOC_CLIENT_LOC);
2386 		loc = loc->next;
2387 
2388 		loc->next = NULL;
2389 		loc->name = CopyString (name, TAGMALLOC_CLIENT_LOC);
2390 		VectorSet (loc->location, (float)atoi(x)/8.0f, (float)atoi(y)/8.0f, (float)atoi(z)/8.0f);
2391 
2392 		//Com_DPrintf ("CL_AddLoc: adding location '%s'\n", name);
2393 
2394 		//advance past \0
2395 		line++;
2396 	}
2397 
2398 	Com_Printf ("Read %d map locations from '%s'\n", LOG_CLIENT, linenum, filename);
2399 
2400 	Z_Free (locBuffer);
2401 	return true;
2402 }
2403 
CL_Loc_Get(vec3_t org)2404 const char *CL_Loc_Get (vec3_t org)
2405 {
2406 	vec3_t			distance;
2407 	uint32			length, bestlength = 0xFFFFFFFF;
2408 	cl_location_t	*loc = &cl_locations, *best = &cl_locations;
2409 
2410 	Q_assert (cl_locations.next);
2411 
2412 	while (loc->next)
2413 	{
2414 		loc = loc->next;
2415 
2416 		VectorSubtract (loc->location, org, distance);
2417 		length = (int)VectorLength (distance);
2418 
2419 		if (length < bestlength)
2420 		{
2421 			best = loc;
2422 			bestlength = length;
2423 		}
2424 	}
2425 
2426 	return best->name;
2427 }
2428 
CL_AddLoc_f(void)2429 void CL_AddLoc_f (void)
2430 {
2431 	cl_location_t	*loc, *last, *newentry;
2432 
2433 	if (Cmd_Argc() < 2)
2434 	{
2435 		Com_Printf ("Purpose: Add a new location.\n"
2436 					"Syntax : addloc <name>\n"
2437 					"Example: addloc red base\n", LOG_CLIENT);
2438 		return;
2439 	}
2440 
2441 	if (cls.state < ca_active)
2442 	{
2443 		Com_Printf ("Not connected.\n", LOG_GENERAL);
2444 		return;
2445 	}
2446 
2447 	loc = &cl_locations;
2448 
2449 	last = loc->next;
2450 
2451 	newentry = Z_TagMalloc (sizeof(*loc), TAGMALLOC_CLIENT_LOC);
2452 	newentry->name = CopyString (Cmd_Args(), TAGMALLOC_CLIENT_LOC);
2453 	FastVectorCopy (cl.refdef.vieworg, newentry->location);
2454 	newentry->next = last;
2455 	loc->next = newentry;
2456 
2457 	Com_Printf ("Location '%s' added at (%d, %d, %d).\n", LOG_CLIENT, newentry->name, (int)cl.refdef.vieworg[0]*8, (int)cl.refdef.vieworg[1]*8, (int)cl.refdef.vieworg[2]*8);
2458 }
2459 
CL_SaveLoc_f(void)2460 void CL_SaveLoc_f (void)
2461 {
2462 	cl_location_t	*loc = &cl_locations;
2463 	FILE			*out;
2464 
2465 	char			locbuff[MAX_QPATH+4];
2466 	char			locfile[MAX_OSPATH];
2467 
2468 	if (!cl_locations.next)
2469 	{
2470 		Com_Printf ("No locations defined.\n", LOG_GENERAL);
2471 		return;
2472 	}
2473 
2474 	COM_StripExtension (cl.configstrings[CS_MODELS+1], locbuff);
2475 	strcat (locbuff, ".loc");
2476 
2477 	Com_sprintf (locfile, sizeof(locfile), "%s/%s", FS_Gamedir(), locbuff);
2478 
2479 	out = fopen (locfile, "wb");
2480 	if (!out)
2481 	{
2482 		Com_Printf ("CL_SaveLoc_f: Unable to open '%s' for writing.\n", LOG_CLIENT, locfile);
2483 		return;
2484 	}
2485 
2486 	while (loc->next)
2487 	{
2488 		loc = loc->next;
2489 		fprintf (out, "%i %i %i %s\n", (int)loc->location[0]*8, (int)loc->location[1]*8, (int)loc->location[2]*8, loc->name);
2490 	}
2491 
2492 	fclose (out);
2493 	Com_Printf ("Locations saved to '%s'.\n", LOG_CLIENT, locbuff);
2494 }
2495 
2496 void vectoangles2 (vec3_t value1, vec3_t angles);
2497 
CL_Get_Loc_There(void)2498 const char *CL_Get_Loc_There (void)
2499 {
2500 	trace_t		tr;
2501 	vec3_t		end;
2502 
2503 	if (!cl_locations.next)
2504 		return cl_default_location->string;
2505 
2506 	end[0] = cl.refdef.vieworg[0] + cl.v_forward[0] * 65556 + cl.v_right[0];
2507 	end[1] = cl.refdef.vieworg[1] + cl.v_forward[1] * 65556 + cl.v_right[1];
2508 	end[2] = cl.refdef.vieworg[2] + cl.v_forward[2] * 65556 + cl.v_right[2];
2509 
2510 	tr = CM_BoxTrace (cl.refdef.vieworg, end, vec3_origin, vec3_origin, 0, MASK_SOLID);
2511 
2512 	if (tr.fraction != 1.0f)
2513 		return CL_Loc_Get (tr.endpos);
2514 
2515 	return CL_Loc_Get (end);
2516 }
2517 
CL_Get_Loc_Here(void)2518 const char *CL_Get_Loc_Here (void)
2519 {
2520 	if (!cl_locations.next)
2521 		return cl_default_location->string;
2522 
2523 	return CL_Loc_Get (cl.refdef.vieworg);
2524 }
2525 
2526 /*void CL_Say_Preprocessor (void)
2527 {
2528 	char *location_name, *p;
2529 	char *say_text;
2530 
2531 	say_text = p = Cmd_Args();
2532 
2533 	if (cl_locations.next)
2534 	{
2535 		while (say_text[0] && say_text[1])
2536 		{
2537 			if ((say_text[0] == '%' && say_text[1] == 'l') || (say_text[0] == '@' && say_text[1] == 't'))
2538 			{
2539 				int location_len, cmd_len;
2540 
2541 				if (say_text[1] == 'l')
2542 				{
2543 					location_name = CL_Loc_Get (cl.refdef.vieworg);
2544 				}
2545 				else
2546 				{
2547 					trace_t		tr;
2548 					vec3_t		end;
2549 
2550 					end[0] = cl.refdef.vieworg[0] + cl.v_forward[0] * 65556 + cl.v_right[0];
2551 					end[1] = cl.refdef.vieworg[1] + cl.v_forward[1] * 65556 + cl.v_right[1];
2552 					end[2] = cl.refdef.vieworg[2] + cl.v_forward[2] * 65556 + cl.v_right[2];
2553 
2554 					tr = CM_BoxTrace (cl.refdef.vieworg, end, vec3_origin, vec3_origin, 0, MASK_SOLID);
2555 
2556 					if (tr.fraction != 1.0f)
2557 						location_name = CL_Loc_Get (tr.endpos);
2558 					else
2559 						location_name = CL_Loc_Get (end);
2560 				}
2561 
2562 				cmd_len = (int)strlen(Cmd_Args());
2563 				location_len = (int)strlen(location_name);
2564 
2565 				if (cmd_len + location_len >= MAX_STRING_CHARS-1)
2566 				{
2567 					Com_DPrintf ("CL_Say_Preprocessor: location expansion aborted, not enough space\n");
2568 					break;
2569 				}
2570 
2571 				memmove (say_text + location_len, say_text + 2, cmd_len - (say_text - p) - 1);
2572 				memcpy (say_text, location_name, location_len);
2573 				say_text += location_len;
2574 			}
2575 			say_text++;
2576 		}
2577 	}
2578 
2579 	Cmd_ForwardToServer ();
2580 }*/
2581 
colortext(const char * text)2582 const char *colortext(const char *text)
2583 {
2584 	static char ctext[2][80];
2585 	static int c=0;
2586 	const char *p;
2587 	char *cp;
2588 	c^=1;
2589 	if (!text[0])
2590 		return text;
2591 
2592 	for (p=text, cp=ctext[c];p[0]!=0;p++, cp++){
2593 		cp[0]=p[0]|128;
2594 	}
2595 	cp[0]=0;
2596 	return ctext[c];
2597 }
2598 
CL_ResetPrecacheCheck(void)2599 void CL_ResetPrecacheCheck (void)
2600 {
2601 	precache_start_time = Sys_Milliseconds();
2602 
2603 	precache_check = CS_MODELS;
2604 	precache_model = 0;
2605 	precache_model_skin = -1;
2606 }
2607 
CL_RequestNextDownload(void)2608 void CL_RequestNextDownload (void)
2609 {
2610 	int			PLAYER_MULT;
2611 	const char	*sexedSounds[MAX_SOUNDS];
2612 	uint32		map_checksum;		// for detecting cheater maps
2613 	char		fn[MAX_OSPATH];
2614 
2615 	char		*cmd;
2616 	dmdl_t		*pheader;
2617 	dsprite_t	*spriteheader;
2618 
2619 	int			i;
2620 
2621 	if (cls.state != ca_connected)
2622 		return;
2623 
2624 	PLAYER_MULT = 0;
2625 
2626 	for (i = CS_SOUNDS; i < CS_SOUNDS + MAX_SOUNDS; i++)
2627 	{
2628 		if (cl.configstrings[i][0] == '*')// && !strstr (cl.configstrings[i], ".."))
2629 			sexedSounds[PLAYER_MULT++] = cl.configstrings[i] + 1;
2630 	}
2631 
2632 	PLAYER_MULT += 5;
2633 
2634 	if (!allow_download->intvalue && precache_check < ENV_CNT)
2635 		precache_check = ENV_CNT;
2636 
2637 //ZOID
2638 	if (precache_check == CS_MODELS)
2639 	{ // confirm map
2640 		precache_check = CS_MODELS+2; // 0 isn't used
2641 		if (allow_download_maps->intvalue)
2642 		{
2643 			if (strlen(cl.configstrings[CS_MODELS+1]) >= MAX_QPATH-1)
2644 				Com_Error (ERR_DROP, "Bad map configstring '%s'", cl.configstrings[CS_MODELS+1]);
2645 
2646 			if (!CL_CheckOrDownloadFile(cl.configstrings[CS_MODELS+1]))
2647 				return; // started a download
2648 		}
2649 	}
2650 
2651 
2652 redoSkins:;
2653 
2654 	if (precache_check >= CS_MODELS && precache_check < CS_MODELS+MAX_MODELS)
2655 	{
2656 		if (allow_download_models->intvalue)
2657 		{
2658 			char *skinname;
2659 
2660 			while (precache_check < CS_MODELS+MAX_MODELS &&
2661 				cl.configstrings[precache_check][0])
2662 			{
2663 				//its a brush/alias model, we don't do those
2664 				if (cl.configstrings[precache_check][0] == '*' || cl.configstrings[precache_check][0] == '#')
2665 				{
2666 					precache_check++;
2667 					continue;
2668 				}
2669 
2670 				//new model, try downloading it
2671 				if (precache_model_skin == -1)
2672 				{
2673 					if (!CL_CheckOrDownloadFile(cl.configstrings[precache_check]))
2674 					{
2675 						precache_check++;
2676 						return; // started a download
2677 					}
2678 					precache_check++;
2679 				}
2680 				else
2681 				{
2682 					//model is ok, now we are checking for skins in the model
2683 					if (!precache_model)
2684 					{
2685 						//load model into buffer
2686 						precache_model_skin = 1;
2687 						FS_LoadFile (cl.configstrings[precache_check], (void **)&precache_model);
2688 						if (!precache_model)
2689 						{
2690 							//shouldn't happen?
2691 							precache_model_skin = 0;
2692 							precache_check++;
2693 							continue; // couldn't load it
2694 						}
2695 
2696 						//is it an alias model
2697 						if (LittleLong(*(uint32 *)precache_model) != IDALIASHEADER)
2698 						{
2699 							//is it a sprite
2700 							if (LittleLong(*(uint32 *)precache_model) != IDSPRITEHEADER)
2701 							{
2702 								//no, free and move onto next model
2703 								FS_FreeFile(precache_model);
2704 								precache_model = NULL;
2705 								precache_model_skin = 0;
2706 								precache_check++;
2707 								continue;
2708 							}
2709 							else
2710 							{
2711 								//get sprite header
2712 								spriteheader = (dsprite_t *)precache_model;
2713 								if (LittleLong (spriteheader->version != SPRITE_VERSION))
2714 								{
2715 									//this is unknown version! free and move onto next.
2716 									FS_FreeFile(precache_model);
2717 									precache_model = NULL;
2718 									precache_check++;
2719 									precache_model_skin = 0;
2720 									continue; // couldn't load it
2721 								}
2722 							}
2723 						}
2724 						else
2725 						{
2726 							//get model header
2727 							pheader = (dmdl_t *)precache_model;
2728 							if (LittleLong (pheader->version) != ALIAS_VERSION)
2729 							{
2730 								//unknown version! free and move onto next
2731 								FS_FreeFile(precache_model);
2732 								precache_model = NULL;
2733 								precache_check++;
2734 								precache_model_skin = 0;
2735 								continue; // couldn't load it
2736 							}
2737 						}
2738 					}
2739 
2740 					//if its an alias model
2741 					if (LittleLong(*(uint32 *)precache_model) == IDALIASHEADER)
2742 					{
2743 						pheader = (dmdl_t *)precache_model;
2744 
2745 						//iterate through number of skins
2746 						while (precache_model_skin - 1 < LittleLong(pheader->num_skins))
2747 						{
2748 							skinname = (char *)precache_model +
2749 								LittleLong(pheader->ofs_skins) +
2750 								(precache_model_skin - 1)*MAX_SKINNAME;
2751 
2752 							//r1: spam warning for models that are broken
2753 							if (strchr (skinname, '\\'))
2754 							{
2755 								Com_Printf ("Warning, model %s with incorrectly linked skin: %s\n", LOG_CLIENT|LOG_WARNING, cl.configstrings[precache_check], skinname);
2756 							}
2757 							else if (strlen(skinname) > MAX_SKINNAME-1)
2758 							{
2759 								Com_Error (ERR_DROP, "Model %s has too long a skin path: %s", cl.configstrings[precache_check], skinname);
2760 							}
2761 
2762 							//check if this skin exists
2763 							if (!CL_CheckOrDownloadFile(skinname))
2764 							{
2765 								precache_model_skin++;
2766 								return; // started a download
2767 							}
2768 							precache_model_skin++;
2769 						}
2770 					}
2771 					else
2772 					{
2773 						//its a sprite
2774 						spriteheader = (dsprite_t *)precache_model;
2775 
2776 						//iterate through skins
2777 						while (precache_model_skin - 1 < LittleLong(spriteheader->numframes))
2778 						{
2779 							skinname = spriteheader->frames[(precache_model_skin - 1)].name;
2780 
2781 							//r1: spam warning for models that are broken
2782 							if (strchr (skinname, '\\'))
2783 							{
2784 								Com_Printf ("Warning, sprite %s with incorrectly linked skin: %s\n", LOG_CLIENT|LOG_WARNING, cl.configstrings[precache_check], skinname);
2785 							}
2786 							else if (strlen(skinname) > MAX_SKINNAME-1)
2787 							{
2788 								Com_Error (ERR_DROP, "Sprite %s has too long a skin path: %s", cl.configstrings[precache_check], skinname);
2789 							}
2790 
2791 							//check if this skin exists
2792 							if (!CL_CheckOrDownloadFile(skinname))
2793 							{
2794 								precache_model_skin++;
2795 								return; // started a download
2796 							}
2797 							precache_model_skin++;
2798 						}
2799 					}
2800 
2801 					//we're done checking the model and all skins, free
2802 					if (precache_model)
2803 					{
2804 						FS_FreeFile(precache_model);
2805 						precache_model = NULL;
2806 					}
2807 
2808 					precache_model_skin = 0;
2809 					precache_check++;
2810 				}
2811 			}
2812 		}
2813 		if (precache_model_skin == -1)
2814 		{
2815 			precache_check = CS_MODELS + 2;
2816 			precache_model_skin = 0;
2817 
2818 	//pending downloads (models), let's wait here before we can check skins.
2819 #ifdef USE_CURL
2820 		if (CL_PendingHTTPDownloads ())
2821 			return;
2822 #endif
2823 
2824 			goto redoSkins;
2825 		}
2826 		precache_check = CS_SOUNDS;
2827 	}
2828 
2829 	if (precache_check >= CS_SOUNDS && precache_check < CS_SOUNDS+MAX_SOUNDS)
2830 	{
2831 		if (allow_download_sounds->intvalue)
2832 		{
2833 			if (precache_check == CS_SOUNDS)
2834 				precache_check++; // zero is blank
2835 
2836 			while (precache_check < CS_SOUNDS+MAX_SOUNDS &&
2837 				cl.configstrings[precache_check][0])
2838 			{
2839 				if (cl.configstrings[precache_check][0] == '*')
2840 				{
2841 					precache_check++;
2842 					continue;
2843 				}
2844 
2845 				if (cl.configstrings[precache_check][0] == '#')
2846 					Com_sprintf(fn, sizeof(fn), "%s", cl.configstrings[precache_check++] + 1);
2847 				else
2848 					Com_sprintf(fn, sizeof(fn), "sound/%s", cl.configstrings[precache_check++]);
2849 
2850 				if (!CL_CheckOrDownloadFile(fn))
2851 					return; // started a download
2852 			}
2853 		}
2854 		precache_check = CS_IMAGES;
2855 	}
2856 
2857 	if (precache_check >= CS_IMAGES && precache_check < CS_IMAGES+MAX_IMAGES)
2858 	{
2859 		if (precache_check == CS_IMAGES)
2860 			precache_check++; // zero is blank
2861 
2862 		while (precache_check < CS_IMAGES+MAX_IMAGES && cl.configstrings[precache_check][0])
2863 		{
2864 			Com_sprintf(fn, sizeof(fn), "pics/%s.pcx", cl.configstrings[precache_check]);
2865 			if (FS_LoadFile (fn, NULL) == -1)
2866 			{
2867 				//Com_sprintf(fn, sizeof(fn), "pics/%s.pcx", cl.configstrings[precache_check]);
2868 				if (!CL_CheckOrDownloadFile(fn))
2869 				{
2870 					precache_check++;
2871 					return; // started a download
2872 				}
2873 			}
2874 			precache_check++;
2875 		}
2876 		precache_check = CS_PLAYERSKINS;
2877 	}
2878 
2879 	// skins are special, since a player has three things to download:
2880 	// model, weapon model and skin
2881 	// so precache_check is now *3
2882 	if (precache_check >= CS_PLAYERSKINS && precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
2883 	{
2884 		if (allow_download_players->intvalue)
2885 		{
2886 			while (precache_check < CS_PLAYERSKINS + MAX_CLIENTS * PLAYER_MULT)
2887 			{
2888 				int i, n, j, length;
2889 				char model[MAX_QPATH], skin[MAX_QPATH], *p;
2890 
2891 				i = (precache_check - CS_PLAYERSKINS)/PLAYER_MULT;
2892 				n = (precache_check - CS_PLAYERSKINS)%PLAYER_MULT;
2893 
2894 				if (i >= cl.maxclients)
2895 				{
2896 					precache_check = ENV_CNT;
2897 					continue;
2898 				}
2899 
2900 				if (!cl.configstrings[CS_PLAYERSKINS+i][0])
2901 				{
2902 					precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2903 					continue;
2904 				}
2905 
2906 				if (n && cls.failed_download)
2907 				{
2908 					cls.failed_download = false;
2909 					precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2910 					continue;
2911 				}
2912 
2913 				if ((p = strchr(cl.configstrings[CS_PLAYERSKINS+i], '\\')) != NULL)
2914 				{
2915 					p++;
2916 				}
2917 				else
2918 				{
2919 					precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2920 					continue;
2921 				}
2922 
2923 				Q_strncpy(model, p, sizeof(model)-1);
2924 
2925 				p = strchr(model, '/');
2926 
2927 				if (!p)
2928 					p = strchr(model, '\\');
2929 
2930 				if (p)
2931 				{
2932 					*p++ = 0;
2933 					if (!p[0] || !model[0])
2934 					{
2935 						precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2936 						continue;
2937 					}
2938 					else
2939 					{
2940 						Q_strncpy (skin, p, sizeof(skin)-1);
2941 					}
2942 				}
2943 				else
2944 				{
2945 					precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2946 					continue;
2947 				}
2948 					//*skin = 0;
2949 
2950 				length = (int)strlen (model);
2951 				for (j = 0; j < length; j++)
2952 				{
2953 					if (!isvalidchar(model[j]))
2954 					{
2955 						Com_Printf ("Bad character '%c' in playermodel '%s'\n", LOG_CLIENT|LOG_WARNING, model[j], model);
2956 						precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2957 						goto skipplayer;
2958 					}
2959 				}
2960 
2961 				length = (int)strlen (skin);
2962 				for (j = 0; j < length; j++)
2963 				{
2964 					if (!isvalidchar(skin[j]))
2965 					{
2966 						Com_Printf ("Bad character '%c' in playerskin '%s'\n", LOG_CLIENT|LOG_WARNING, skin[j], skin);
2967 						precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
2968 						goto skipplayer;
2969 					}
2970 				}
2971 
2972 				switch (n)
2973 				{
2974 					case -1:
2975 						continue;
2976 					case 0: // model
2977 						cls.failed_download = false;
2978 						Com_sprintf(fn, sizeof(fn), "players/%s/tris.md2", model);
2979 						if (!CL_CheckOrDownloadFile(fn)) {
2980 							precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 1;
2981 							return; // started a download
2982 						}
2983 						//n++;
2984 						/*FALL THROUGH*/
2985 
2986 					case 1: // weapon model
2987 						Com_sprintf(fn, sizeof(fn), "players/%s/weapon.md2", model);
2988 						if (!CL_CheckOrDownloadFile(fn)) {
2989 							precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 2;
2990 							return; // started a download
2991 						}
2992 						//n++;
2993 						/*FALL THROUGH*/
2994 
2995 					case 2: // weapon skin
2996 						Com_sprintf(fn, sizeof(fn), "players/%s/weapon.pcx", model);
2997 						if (!CL_CheckOrDownloadFile(fn)) {
2998 							precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 3;
2999 							return; // started a download
3000 						}
3001 						//n++;
3002 						/*FALL THROUGH*/
3003 
3004 					case 3: // skin
3005 						Com_sprintf(fn, sizeof(fn), "players/%s/%s.pcx", model, skin);
3006 						if (!CL_CheckOrDownloadFile(fn)) {
3007 							precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 4;
3008 							return; // started a download
3009 						}
3010 						//n++;
3011 						/*FALL THROUGH*/
3012 
3013 					case 4: // skin_i
3014 						Com_sprintf(fn, sizeof(fn), "players/%s/%s_i.pcx", model, skin);
3015 						if (!CL_CheckOrDownloadFile(fn)) {
3016 							precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + 5;
3017 							return; // started a download
3018 						}
3019 						n = 5;
3020 
3021 					default:
3022 						while (n < PLAYER_MULT)
3023 						{
3024 							Com_sprintf(fn, sizeof(fn), "players/%s/%s", model, sexedSounds[n-5]);
3025 							n++;
3026 							if (!CL_CheckOrDownloadFile(fn)) {
3027 								precache_check = CS_PLAYERSKINS + i * PLAYER_MULT + n;
3028 								return; // started a download
3029 							}
3030 						}
3031 				}
3032 
3033 				// move on to next model
3034 				precache_check = CS_PLAYERSKINS + (i + 1) * PLAYER_MULT;
3035 
3036 skipplayer:;
3037 			}
3038 		}
3039 		// precache phase completed
3040 		precache_check = ENV_CNT;
3041 	}
3042 
3043 #ifdef USE_CURL
3044 		//map might still be downloading?
3045 		if (CL_PendingHTTPDownloads ())
3046 			return;
3047 #endif
3048 
3049  	if (precache_check == ENV_CNT)
3050 	{
3051 		char locbuff[MAX_QPATH+4];
3052 		precache_check = ENV_CNT + 1;
3053 
3054 		COM_StripExtension (cl.configstrings[CS_MODELS+1], locbuff);
3055 		strcat (locbuff, ".loc");
3056 
3057 		//try basedir/moddir/maps/mapname.loc and basedir/locs/mapname.loc
3058 		if (!CL_LoadLoc (locbuff))
3059 		{
3060 			locbuff[0] = 'l';
3061 			locbuff[1] = 'o';
3062 			locbuff[2] = 'c';
3063 			locbuff[3] = 's';
3064 			CL_LoadLoc (locbuff);
3065 		}
3066 
3067 		CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
3068 
3069 		if (map_checksum && map_checksum != strtoul(cl.configstrings[CS_MAPCHECKSUM], NULL, 10)) {
3070 			Com_Error (ERR_DROP, "Local map version differs from server: 0x%.8x != 0x%.8x",
3071 				map_checksum, atoi(cl.configstrings[CS_MAPCHECKSUM]));
3072 			return;
3073 		}
3074 	}
3075 
3076 	if (precache_check > ENV_CNT && precache_check < TEXTURE_CNT) {
3077 		if (allow_download->intvalue && allow_download_maps->intvalue) {
3078 			while (precache_check < TEXTURE_CNT) {
3079 				int n = precache_check++ - ENV_CNT - 1;
3080 
3081 				if (n & 1)
3082 					Com_sprintf(fn, sizeof(fn), "env/%s%s.pcx",
3083 						cl.configstrings[CS_SKY], env_suf[n/2]);
3084 				else
3085 					Com_sprintf(fn, sizeof(fn), "env/%s%s.tga",
3086 						cl.configstrings[CS_SKY], env_suf[n/2]);
3087 				if (!CL_CheckOrDownloadFile(fn))
3088 					return; // started a download
3089 			}
3090 		}
3091 		precache_check = TEXTURE_CNT;
3092 	}
3093 
3094 	if (precache_check == TEXTURE_CNT) {
3095 		precache_check = TEXTURE_CNT+1;
3096 		precache_tex = 0;
3097 	}
3098 
3099 	// confirm existance of textures, download any that don't exist
3100 	if (precache_check == TEXTURE_CNT+1) {
3101 		// from qcommon/cmodel.c
3102 		extern int			numtexinfo;
3103 		extern mapsurface_t	map_surfaces[];
3104 
3105 		if (allow_download->intvalue && allow_download_maps->intvalue) {
3106 			while (precache_tex < numtexinfo) {
3107 				//char fn[MAX_OSPATH];
3108 
3109 				//r1: spam warning about texture
3110 				if (strchr (map_surfaces[precache_tex].rname, '\\'))
3111 					Com_Printf ("Warning, incorrectly referenced texture '%s'\n", LOG_CLIENT|LOG_WARNING, map_surfaces[precache_tex].rname);
3112 				else if (!strchr (map_surfaces[precache_tex].rname, '/'))
3113 					Com_Printf ("Warning, texture '%s' not in a sub-directory\n", LOG_CLIENT|LOG_WARNING, map_surfaces[precache_tex].rname);
3114 
3115 				Com_sprintf(fn, sizeof(fn), "textures/%s.wal", map_surfaces[precache_tex++].rname);
3116 				if (!CL_CheckOrDownloadFile(fn))
3117 					return; // started a download
3118 			}
3119 		}
3120 		precache_check = TEXTURE_CNT+999;
3121 	}
3122 
3123 	//pending downloads (possibly textures), let's wait here.
3124 #ifdef USE_CURL
3125 	if (CL_PendingHTTPDownloads ())
3126 		return;
3127 #endif
3128 
3129 //ZOID
3130 	CL_RegisterSounds ();
3131 	CL_PrepRefresh ();
3132 
3133 	Com_DPrintf ("Precache completed in %u msec.\n", Sys_Milliseconds() - precache_start_time);
3134 
3135 	if (cl_async->intvalue && cl_maxfps->intvalue > 100)
3136 		Com_Printf ("\n%s\nR1Q2 separates network and rendering so a cl_maxfps value > 30\nis rarely needed. Use r_maxfps to control maximum rendered FPS\n\n", LOG_CLIENT, colortext ("WARNING: A cl_maxfps value of over 100 is strongly discouraged"));
3137 
3138 	if (cl_autorecord->intvalue)
3139 	{
3140 		char	autorecord_name[MAX_QPATH];
3141 		char	mapname[MAX_QPATH];
3142 		char	time_buff[32];
3143 
3144 		time_t	tm;
3145 
3146 		time(&tm);
3147 		strftime(time_buff, sizeof(time_buff)-1, "%Y-%m-%d-%H%M", localtime(&tm));
3148 
3149 		COM_StripExtension (cl.configstrings[CS_MODELS+1], mapname);
3150 
3151 		Com_sprintf (autorecord_name, sizeof(autorecord_name), "%s/demos/%s-%s.dm2", FS_Gamedir(), time_buff, mapname + 5);
3152 
3153 		if (CL_BeginRecording (autorecord_name))
3154 			Com_Printf ("Auto-recording to %s.\n", LOG_CLIENT, autorecord_name);
3155 		else
3156 			Com_Printf ("Couldn't auto-record to %s.\n", LOG_CLIENT, autorecord_name);
3157 	}
3158 
3159 	CL_FixCvarCheats();
3160 
3161 	if (cls.serverProtocol == PROTOCOL_R1Q2)
3162 	{
3163 		MSG_BeginWriting (clc_setting);
3164 		MSG_WriteShort (CLSET_NOGUN);
3165 		MSG_WriteShort (cl_gun->intvalue ? 0 : 1);
3166 		MSG_EndWriting (&cls.netchan.message);
3167 
3168 		MSG_BeginWriting (clc_setting);
3169 		MSG_WriteShort (CLSET_PLAYERUPDATE_REQUESTS);
3170 		MSG_WriteShort (cl_player_updates->intvalue);
3171 		MSG_EndWriting (&cls.netchan.message);
3172 
3173 		MSG_BeginWriting (clc_setting);
3174 		MSG_WriteShort (CLSET_FPS);
3175 		MSG_WriteShort (cl_updaterate->intvalue);
3176 		MSG_EndWriting (&cls.netchan.message);
3177 	}
3178 
3179 	MSG_WriteByte (clc_stringcmd);
3180 	MSG_WriteString (va("begin %i\n", precache_spawncount) );
3181 	MSG_EndWriting (&cls.netchan.message);
3182 
3183 	cmd = Cmd_MacroExpandString("$cl_beginmapcmd");
3184 	if (cmd)
3185 	{
3186 		Cbuf_AddText (cmd);
3187 		Cbuf_AddText ("\n");
3188 		Cbuf_Execute ();
3189 	}
3190 	else
3191 		Com_Printf ("WARNING: Error expanding $cl_beginmapcmd, ignored.\n", LOG_CLIENT|LOG_WARNING);
3192 
3193 	send_packet_now = true;
3194 }
3195 
3196 /*
3197 =================
3198 CL_Precache_f
3199 
3200 The server will send this command right
3201 before allowing the client into the server
3202 =================
3203 */
CL_Precache_f(void)3204 void CL_Precache_f (void)
3205 {
3206 	//Yet another hack to let old demos work
3207 	//the old precache sequence
3208 	if (Cmd_Argc() < 2)
3209 	{
3210 		uint32		map_checksum;		// for detecting cheater maps
3211 		CM_LoadMap (cl.configstrings[CS_MODELS+1], true, &map_checksum);
3212 		CL_RegisterSounds ();
3213 		CL_PrepRefresh ();
3214 		return;
3215 	}
3216 
3217 	precache_spawncount = atoi(Cmd_Argv(1));
3218 	CL_ResetPrecacheCheck ();
3219 	CL_RequestNextDownload();
3220 }
3221 
CL_Toggle_f(void)3222 void CL_Toggle_f (void)
3223 {
3224 	cvar_t *tvar;
3225 
3226 	if (Cmd_Argc() < 2)
3227 	{
3228 		Com_Printf ("Usage: toggle cvar [option1 option2 option3 ...]\n", LOG_CLIENT);
3229 		return;
3230 	}
3231 
3232 	tvar = Cvar_FindVar (Cmd_Argv(1));
3233 
3234 	if (!tvar)
3235 	{
3236 		Com_Printf ("%s: no such variable\n", LOG_CLIENT, Cmd_Argv(1));
3237 		return;
3238 	}
3239 
3240 	if (Cmd_Argc() == 2)
3241 	{
3242 		if (tvar->value != 0 && tvar->value != 1)
3243 		{
3244 			Com_Printf ("%s: not a binary variable\n", LOG_CLIENT, Cmd_Argv(1));
3245 			return;
3246 		}
3247 		Cvar_SetValue (Cmd_Argv(1), (float)((int)tvar->value ^ 1));
3248 	}
3249 	else
3250 	{
3251 		int i;
3252 
3253 		for (i = 2; i < Cmd_Argc(); i++)
3254 		{
3255 			if (!strcmp (tvar->string, Cmd_Argv(i)))
3256 			{
3257 				if (i == Cmd_Argc() -1)
3258 				{
3259 					Cvar_Set (Cmd_Argv(1), Cmd_Argv(2));
3260 					break;
3261 				}
3262 				else
3263 				{
3264 					Cvar_Set (Cmd_Argv(1), Cmd_Argv(i+1));
3265 					break;
3266 				}
3267 			}
3268 		}
3269 	}
3270 }
3271 
version_update(cvar_t * self,char * old,char * newValue)3272 void version_update (cvar_t *self, char *old, char *newValue)
3273 {
3274 	Cvar_Set ("cl_version", va(PRODUCTNAME " " VERSION "; %s", newValue[0] ? newValue : "unknown renderer"));
3275 }
3276 
_name_changed(cvar_t * var,char * oldValue,char * newValue)3277 void _name_changed (cvar_t *var, char *oldValue, char *newValue)
3278 {
3279 	if (strlen(newValue) >= 16)
3280 	{
3281 		newValue[15] = 0;
3282 	}
3283 	else if (!*newValue)
3284 	{
3285 		Cvar_Set ("name", "unnamed");
3286 	}
3287 }
3288 
_maxfps_changed(cvar_t * var,char * oldValue,char * newValue)3289 void _maxfps_changed (cvar_t *var, char *oldValue, char *newValue)
3290 {
3291 	if (var->intvalue < 5)
3292 	{
3293 		Cvar_Set (var->name, "5");
3294 	}
3295 
3296 	if (cl_async->intvalue == 0)
3297 	{
3298 		if (var == cl_maxfps)
3299 			Cvar_SetValue ("r_maxfps", var->value);
3300 		else if (var == r_maxfps)
3301 			Cvar_SetValue ("cl_maxfps", var->value);
3302 	}
3303 }
3304 
_async_changed(cvar_t * var,char * oldValue,char * newValue)3305 void _async_changed (cvar_t *var, char *oldValue, char *newValue)
3306 {
3307 	if (var->intvalue == 0)
3308 	{
3309 		Cvar_SetValue ("r_maxfps", cl_maxfps->value);
3310 	}
3311 }
3312 
_railtrail_changed(cvar_t * var,char * oldValue,char * newValue)3313 void _railtrail_changed (cvar_t *var, char *oldValue, char *newValue)
3314 {
3315 	if (var->intvalue > 254)
3316 		Cvar_Set (var->name, "254");
3317 	else if (var->intvalue < 0)
3318 		Cvar_Set (var->name, "0");
3319 }
3320 
_protocol_changed(cvar_t * var,char * oldValue,char * newValue)3321 void _protocol_changed (cvar_t *var, char *oldValue, char *newValue)
3322 {
3323 	//force reparsing of cl_protocol
3324 	if (cls.state == ca_disconnected)
3325 		cls.serverProtocol = 0;
3326 }
3327 
CL_FollowIP_f(void)3328 void CL_FollowIP_f (void)
3329 {
3330 	if (!cls.followHost[0])
3331 	{
3332 		Com_Printf ("No IP to follow.\n", LOG_GENERAL);
3333 		return;
3334 	}
3335 
3336 	Cbuf_AddText (va("connect \"%s\"\n", cls.followHost));
3337 }
3338 
CL_IndexStats_f(void)3339 void CL_IndexStats_f (void)
3340 {
3341 	int	i;
3342 	int	count;
3343 /*
3344 #define	CS_SOUNDS			(CS_MODELS+MAX_MODELS)			//288
3345 #define	CS_IMAGES			(CS_SOUNDS+MAX_SOUNDS)			//544
3346 #define	CS_LIGHTS			(CS_IMAGES+MAX_IMAGES)			//800
3347 #define	CS_ITEMS			(CS_LIGHTS+MAX_LIGHTSTYLES)		//1056
3348 #define	CS_PLAYERSKINS		(CS_ITEMS+MAX_ITEMS)			//1312
3349 #define CS_GENERAL			(CS_PLAYERSKINS+MAX_CLIENTS)	//1568
3350 #define	MAX_CONFIGSTRINGS	(CS_GENERAL+MAX_GENERAL)		//2080
3351 */
3352 	if (Cmd_Argc() != 1)
3353 	{
3354 		unsigned	offset;
3355 		unsigned	count = atoi(Cmd_Argv(2));
3356 
3357 		if (!Q_stricmp (Cmd_Argv(1), "cs_models"))
3358 			offset = CS_MODELS;
3359 		else if (!Q_stricmp (Cmd_Argv(1), "cs_sounds"))
3360 			offset = CS_SOUNDS;
3361 		else if (!Q_stricmp (Cmd_Argv(1), "cs_images"))
3362 			offset = CS_IMAGES;
3363 		else if (!Q_stricmp (Cmd_Argv(1), "cs_items"))
3364 			offset = CS_ITEMS;
3365 		else if (!Q_stricmp (Cmd_Argv(1), "cs_playerskins"))
3366 			offset = CS_PLAYERSKINS;
3367 		else
3368 			offset = atoi(Cmd_Argv(1));
3369 
3370 		if (offset >= CS_GENERAL + MAX_GENERAL)
3371 		{
3372 			Com_Printf ("Bad offset.\n", LOG_GENERAL);
3373 			return;
3374 		}
3375 
3376 		if (count == 0)
3377 			count = 256;
3378 
3379 		if (offset + count > CS_GENERAL + MAX_GENERAL)
3380 		{
3381 			count = CS_GENERAL + MAX_GENERAL - offset;
3382 		}
3383 
3384 		for (i = offset; i < offset + count; i++)
3385 		{
3386 			Com_Printf ("%3d/%3d: %s\n", LOG_GENERAL, i, i - offset, cl.configstrings[i]);
3387 		}
3388 
3389 		return;
3390 	}
3391 	for (count = 0, i = CS_MODELS; i < CS_MODELS + MAX_MODELS; i++)
3392 	{
3393 		if (cl.configstrings[i][0])
3394 			count++;
3395 	}
3396 	Com_Printf ("CS_MODELS     : %d\n", LOG_GENERAL, count);
3397 
3398 	for (count = 0, i = CS_SOUNDS; i < CS_SOUNDS + MAX_SOUNDS; i++)
3399 	{
3400 		if (cl.configstrings[i][0])
3401 			count++;
3402 	}
3403 	Com_Printf ("CS_SOUNDS     : %d\n", LOG_GENERAL, count);
3404 
3405 	for (count = 0, i = CS_LIGHTS; i < CS_LIGHTS + MAX_LIGHTSTYLES; i++)
3406 	{
3407 		if (cl.configstrings[i][0])
3408 			count++;
3409 	}
3410 	Com_Printf ("CS_LIGHTS     : %d\n", LOG_GENERAL, count);
3411 
3412 	for (count = 0, i = CS_IMAGES; i < CS_IMAGES + MAX_IMAGES; i++)
3413 	{
3414 		if (cl.configstrings[i][0])
3415 			count++;
3416 	}
3417 	Com_Printf ("CS_IMAGES     : %d\n", LOG_GENERAL, count);
3418 
3419 	for (count = 0, i = CS_ITEMS; i < CS_ITEMS + MAX_ITEMS; i++)
3420 	{
3421 		if (cl.configstrings[i][0])
3422 			count++;
3423 	}
3424 	Com_Printf ("CS_ITEMS      : %d\n", LOG_GENERAL, count);
3425 
3426 	for (count = 0, i = CS_PLAYERSKINS; i < CS_PLAYERSKINS + MAX_CLIENTS; i++)
3427 	{
3428 		if (cl.configstrings[i][0])
3429 			count++;
3430 	}
3431 	Com_Printf ("CS_PLAYERSKINS: %d\n", LOG_GENERAL, count);
3432 
3433 	for (count = 0, i = CS_GENERAL; i < CS_GENERAL + MAX_GENERAL; i++)
3434 	{
3435 		if (cl.configstrings[i][0])
3436 			count++;
3437 	}
3438 	Com_Printf ("CS_GENERAL    : %d\n", LOG_GENERAL, count);
3439 }
3440 
_cl_http_max_connections_changed(cvar_t * c,char * old,char * new)3441 void _cl_http_max_connections_changed (cvar_t *c, char *old, char *new)
3442 {
3443 	if (c->intvalue > 8)
3444 		Cvar_Set (c->name, "8");
3445 	else if (c->intvalue < 1)
3446 		Cvar_Set (c->name, "1");
3447 
3448 	//not really needed any more, hopefully no one still uses apache...
3449 	//if (c->intvalue > 2)
3450 	//	Com_Printf ("WARNING: Changing the maximum connections higher than 2 violates the HTTP specification recommendations. Doing so may result in you being blocked from the remote system and offers no performance benefits unless you are on a very high latency link (ie, satellite)\n", LOG_GENERAL);
3451 }
3452 
_gun_changed(cvar_t * c,char * old,char * new)3453 void _gun_changed (cvar_t *c, char *old, char *new)
3454 {
3455 	if (cls.state >= ca_connected && cls.serverProtocol == PROTOCOL_R1Q2)
3456 	{
3457 		MSG_BeginWriting (clc_setting);
3458 		MSG_WriteShort (CLSET_NOGUN);
3459 		MSG_WriteShort (c->intvalue ? 0 : 1);
3460 		MSG_EndWriting (&cls.netchan.message);
3461 	}
3462 }
3463 
_player_updates_changed(cvar_t * c,char * old,char * new)3464 void _player_updates_changed (cvar_t *c, char *old, char *new)
3465 {
3466 	if (c->intvalue > 10)
3467 		Cvar_Set (c->name, "10");
3468 	else if (c->intvalue < 0)
3469 		Cvar_Set (c->name, "0");
3470 
3471 	if (cls.state >= ca_connected && cls.serverProtocol == PROTOCOL_R1Q2)
3472 	{
3473 		MSG_BeginWriting (clc_setting);
3474 		MSG_WriteShort (CLSET_PLAYERUPDATE_REQUESTS);
3475 		MSG_WriteShort (cl_player_updates->intvalue);
3476 		MSG_EndWriting (&cls.netchan.message);
3477 	}
3478 
3479 	if (c->intvalue == 0)
3480 		cl.player_update_time = 0;
3481 }
3482 
_updaterate_changed(cvar_t * c,char * old,char * new)3483 void _updaterate_changed (cvar_t *c, char *old, char *new)
3484 {
3485 	if (c->intvalue < 0)
3486 		Cvar_Set (c->name, "0");
3487 
3488 	if (cls.state >= ca_connected && cls.serverProtocol == PROTOCOL_R1Q2)
3489 	{
3490 		MSG_BeginWriting (clc_setting);
3491 		MSG_WriteShort (CLSET_FPS);
3492 		MSG_WriteShort (cl_updaterate->intvalue);
3493 		MSG_EndWriting (&cls.netchan.message);
3494 	}
3495 }
3496 
3497 /*
3498 =================
3499 CL_InitLocal
3500 =================
3501 */
CL_InitLocal(void)3502 void CL_InitLocal (void)
3503 {
3504 	const char	*glVersion;
3505 
3506 	cls.proxyState = ps_none;
3507 	cls.state = ca_disconnected;
3508 	cls.realtime = Sys_Milliseconds ();
3509 
3510 	CL_InitInput ();
3511 
3512 	adr0 = Cvar_Get( "adr0", "", CVAR_ARCHIVE );
3513 	adr1 = Cvar_Get( "adr1", "", CVAR_ARCHIVE );
3514 	adr2 = Cvar_Get( "adr2", "", CVAR_ARCHIVE );
3515 	adr3 = Cvar_Get( "adr3", "", CVAR_ARCHIVE );
3516 	adr4 = Cvar_Get( "adr4", "", CVAR_ARCHIVE );
3517 	adr5 = Cvar_Get( "adr5", "", CVAR_ARCHIVE );
3518 	adr6 = Cvar_Get( "adr6", "", CVAR_ARCHIVE );
3519 	adr7 = Cvar_Get( "adr7", "", CVAR_ARCHIVE );
3520 	adr8 = Cvar_Get( "adr8", "", CVAR_ARCHIVE );
3521 
3522 //
3523 // register our variables
3524 //
3525 #ifdef CL_STEREO_SUPPORT
3526 	cl_stereo_separation = Cvar_Get( "cl_stereo_separation", "0.4", CVAR_ARCHIVE );
3527 	cl_stereo = Cvar_Get( "cl_stereo", "0", 0 );
3528 #endif
3529 
3530 	cl_add_blend = Cvar_Get ("cl_blend", "1", 0);
3531 	cl_add_lights = Cvar_Get ("cl_lights", "1", 0);
3532 	cl_add_particles = Cvar_Get ("cl_particles", "1", 0);
3533 	cl_add_entities = Cvar_Get ("cl_entities", "1", 0);
3534 	cl_gun = Cvar_Get ("cl_gun", "1", CVAR_ARCHIVE);
3535 	cl_gun->changed = _gun_changed;
3536 
3537 	cl_footsteps = Cvar_Get ("cl_footsteps", "1", 0);
3538 	cl_noskins = Cvar_Get ("cl_noskins", "0", 0);
3539 //	cl_autoskins = Cvar_Get ("cl_autoskins", "0", 0);
3540 	cl_predict = Cvar_Get ("cl_predict", "1", 0);
3541 	cl_backlerp = Cvar_Get ("cl_backlerp", "1", 0);
3542 //	cl_minfps = Cvar_Get ("cl_minfps", "5", 0);
3543 
3544 	r_maxfps = Cvar_Get ("r_maxfps", "1000", 0);
3545 	r_maxfps->changed = _maxfps_changed;
3546 
3547 	cl_maxfps = Cvar_Get ("cl_maxfps", "60", CVAR_ARCHIVE);
3548 	cl_maxfps->changed = _maxfps_changed;
3549 
3550 	cl_async = Cvar_Get ("cl_async", "1", 0);
3551 	cl_async->changed = _async_changed;
3552 
3553 	cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0);
3554 	cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0);
3555 	cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0);
3556 	cl_yawspeed = Cvar_Get ("cl_yawspeed", "140", 0);
3557 	cl_pitchspeed = Cvar_Get ("cl_pitchspeed", "150", 0);
3558 	cl_anglespeedkey = Cvar_Get ("cl_anglespeedkey", "1.5", 0);
3559 
3560 	cl_run = Cvar_Get ("cl_run", "1", CVAR_ARCHIVE);
3561 
3562 	freelook = Cvar_Get( "freelook", "1", CVAR_ARCHIVE );
3563 	lookspring = Cvar_Get ("lookspring", "0", CVAR_ARCHIVE);
3564 	lookstrafe = Cvar_Get ("lookstrafe", "0", CVAR_ARCHIVE);
3565 	sensitivity = Cvar_Get ("sensitivity", "3", CVAR_ARCHIVE);
3566 
3567 	m_pitch = Cvar_Get ("m_pitch", "0.022", CVAR_ARCHIVE);
3568 	m_yaw = Cvar_Get ("m_yaw", "0.022", 0);
3569 	m_forward = Cvar_Get ("m_forward", "1", 0);
3570 	m_side = Cvar_Get ("m_side", "1", 0);
3571 
3572 	cl_shownet = Cvar_Get ("cl_shownet", "0", 0);
3573 	cl_showmiss = Cvar_Get ("cl_showmiss", "0", 0);
3574 	cl_showclamp = Cvar_Get ("showclamp", "0", 0);
3575 	cl_timeout = Cvar_Get ("cl_timeout", "120", 0);
3576 	cl_paused = Cvar_Get ("paused", "0", 0);
3577 	cl_timedemo = Cvar_Get ("timedemo", "0", 0);
3578 
3579 	cl_filterchat = Cvar_Get ("cl_filterchat", "0", 0);
3580 
3581 	rcon_client_password = Cvar_Get ("rcon_password", "", 0);
3582 	rcon_address = Cvar_Get ("rcon_address", "", 0);
3583 
3584 	cl_lightlevel = Cvar_Get ("r_lightlevel", "0", CVAR_NOSET);
3585 
3586 	//
3587 	// userinfo
3588 	//
3589 	info_password = Cvar_Get ("password", "", CVAR_USERINFO);
3590 	info_spectator = Cvar_Get ("spectator", "0", CVAR_USERINFO);
3591 	name = Cvar_Get ("name", "unnamed", CVAR_USERINFO | CVAR_ARCHIVE);
3592 	skin = Cvar_Get ("skin", "male/grunt", CVAR_USERINFO | CVAR_ARCHIVE);
3593 	rate = Cvar_Get ("rate", "15000", CVAR_USERINFO | CVAR_ARCHIVE);	// FIXME
3594 	msg = Cvar_Get ("msg", "1", CVAR_USERINFO | CVAR_ARCHIVE);
3595 	hand = Cvar_Get ("hand", "0", CVAR_USERINFO | CVAR_ARCHIVE);
3596 	fov = Cvar_Get ("fov", "90", CVAR_USERINFO | CVAR_ARCHIVE);
3597 	gender = Cvar_Get ("gender", "male", CVAR_USERINFO | CVAR_ARCHIVE);
3598 	gender_auto = Cvar_Get ("gender_auto", "1", CVAR_ARCHIVE);
3599 	gender->modified = false; // clear this so we know when user sets it manually
3600 
3601 	cl_vwep = Cvar_Get ("cl_vwep", "1", CVAR_ARCHIVE);
3602 
3603 	//r1: only use experimental protocol in debug (not)
3604 #ifdef _DEBUG
3605 	cl_protocol = Cvar_Get ("cl_protocol", "0", 0);
3606 #else
3607 	cl_protocol = Cvar_Get ("cl_protocol", "0", 0);
3608 #endif
3609 
3610 	cl_protocol->changed = _protocol_changed;
3611 
3612 	//cl_defertimer = Cvar_Get ("cl_defertimer", "1", 0);
3613 	cl_smoothsteps = Cvar_Get ("cl_smoothsteps", "3", 0);
3614 	cl_instantpacket = Cvar_Get ("cl_instantpacket", "1", 0);
3615 	cl_nolerp = Cvar_Get ("cl_nolerp", "0", 0);
3616 	cl_instantack = Cvar_Get ("cl_instantack", "0", 0);
3617 	cl_autorecord = Cvar_Get ("cl_autorecord", "0", 0);
3618 
3619 	cl_railtrail = Cvar_Get ("cl_railtrail", "0", 0);
3620 	cl_railtrail->changed = _railtrail_changed;
3621 
3622 	//misc for testing
3623 	cl_test = Cvar_Get ("cl_test", "0", 0);
3624 	cl_test2 = Cvar_Get ("cl_test2", "0", 0);
3625 	cl_test3 = Cvar_Get ("cl_test3", "0", 0);
3626 
3627 	cl_original_dlights = Cvar_Get ("cl_original_dlights", "1", 0);
3628 
3629 	cl_default_location = Cvar_Get ("cl_default_location", "", 0);
3630 
3631 #ifdef _DEBUG
3632 	cl_player_updates = Cvar_Get ("cl_player_updates", "0", 0);
3633 #else
3634 	cl_player_updates = Cvar_Get ("cl_player_updates", "0", CVAR_NOSET);
3635 #endif
3636 
3637 	cl_player_updates->changed = _player_updates_changed;
3638 
3639 	cl_updaterate = Cvar_Get ("cl_updaterate", "0", 0);
3640 	cl_updaterate->changed = _updaterate_changed;
3641 
3642 #ifdef NO_SERVER
3643 	allow_download = Cvar_Get ("allow_download", "1", CVAR_ARCHIVE);
3644 	allow_download_players  = Cvar_Get ("allow_download_players", "1", CVAR_ARCHIVE);
3645 	allow_download_models = Cvar_Get ("allow_download_models", "1", CVAR_ARCHIVE);
3646 	allow_download_sounds = Cvar_Get ("allow_download_sounds", "1", CVAR_ARCHIVE);
3647 	allow_download_maps	  = Cvar_Get ("allow_download_maps", "1", CVAR_ARCHIVE);
3648 #endif
3649 
3650 #ifdef USE_CURL
3651 	cl_http_proxy = Cvar_Get ("cl_http_proxy", "", 0);
3652 	cl_http_filelists = Cvar_Get ("cl_http_filelists", "1", 0);
3653 	cl_http_downloads = Cvar_Get ("cl_http_downloads", "1", 0);
3654 	cl_http_max_connections = Cvar_Get ("cl_http_max_connections", "4", 0);
3655 	cl_http_max_connections->changed = _cl_http_max_connections_changed;
3656 #endif
3657 
3658 	cl_proxy = Cvar_Get ("cl_proxy", "", 0);
3659 
3660 	//haxx
3661 	glVersion = Cvar_VariableString ("cl_version");
3662 	(Cvar_ForceSet ("cl_version", va(PRODUCTNAME " " VERSION "; %s", glVersion[0] ? glVersion : "unknown renderer" )))->changed = version_update;
3663 
3664 	name->changed = _name_changed;
3665 
3666 #ifdef _DEBUG
3667 	dbg_framesleep = Cvar_Get ("dbg_framesleep", "0", 0);
3668 #endif
3669 
3670 	//cl_snaps = Cvar_Get ("cl_snaps", "1", 0);
3671 	//cl_snaps->modified = false;
3672 	//cl_snaps->changed = CL_SnapsMessage;
3673 
3674 	CL_FixCvarCheats ();
3675 
3676 	//
3677 	// register our commands
3678 	//
3679 
3680 	//r1: r1q2 stuff goes here so it never overrides autocomplete order for base q2 cmds
3681 
3682 	Cmd_AddCommand ("followip", CL_FollowIP_f);
3683 	Cmd_AddCommand ("indexstats", CL_IndexStats_f);
3684 
3685 	//r1: allow passive connects
3686 	Cmd_AddCommand ("passive", CL_Passive_f);
3687 
3688 	//r1: toggle cvar
3689 	Cmd_AddCommand ("toggle", CL_Toggle_f);
3690 
3691 	//r1: server status (connectionless)
3692 	Cmd_AddCommand ("serverstatus", CL_ServerStatus_f);
3693 
3694 	Cmd_AddCommand ("ignore", CL_Ignore_f);
3695 	Cmd_AddCommand ("unignore", CL_Unignore_f);
3696 
3697 	//r1: loc support
3698 	Cmd_AddCommand ("addloc", CL_AddLoc_f);
3699 	Cmd_AddCommand ("saveloc", CL_SaveLoc_f);
3700 
3701 #ifdef _DEBUG
3702 	Cmd_AddCommand ("packet", CL_Packet_f);
3703 	Cmd_AddCommand ("spam", CL_Spam_f);
3704 #endif
3705 
3706 #ifdef CLIENT_DLL
3707 	Cmd_AddCommand ("cl_restart", CL_ClDLL_Restart_f);
3708 #endif
3709 
3710 	Cmd_AddCommand ("cmd", CL_ForwardToServer_f);
3711 	Cmd_AddCommand ("pause", CL_Pause_f);
3712 	Cmd_AddCommand ("pingservers", CL_PingServers_f);
3713 	Cmd_AddCommand ("skins", CL_Skins_f);
3714 
3715 	Cmd_AddCommand ("userinfo", CL_Userinfo_f);
3716 	Cmd_AddCommand ("snd_restart", CL_Snd_Restart_f);
3717 
3718 	Cmd_AddCommand ("changing", CL_Changing_f);
3719 	Cmd_AddCommand ("disconnect", CL_Disconnect_f);
3720 	Cmd_AddCommand ("record", CL_Record_f);
3721 	Cmd_AddCommand ("stop", CL_Stop_f);
3722 
3723 	Cmd_AddCommand ("quit", CL_Quit_f);
3724 
3725 	Cmd_AddCommand ("connect", CL_Connect_f);
3726 	Cmd_AddCommand ("reconnect", CL_Reconnect_f);
3727 
3728 	Cmd_AddCommand ("rcon", CL_Rcon_f);
3729 
3730 // 	Cmd_AddCommand ("packet", CL_Packet_f); // this is dangerous to leave in
3731 
3732 	//r1: serves no purpose other than to allow buffer overflows!
3733 //	Cmd_AddCommand ("setenv", CL_Setenv_f );
3734 
3735 	Cmd_AddCommand ("precache", CL_Precache_f);
3736 
3737 	Cmd_AddCommand ("download", CL_Download_f);
3738 
3739 	//
3740 	// forward to server commands
3741 	//
3742 	// the only thing this does is allow command completion
3743 	// to work -- all unknown commands are automatically
3744 	// forwarded to the server
3745 	Cmd_AddCommand ("wave", NULL);
3746 	Cmd_AddCommand ("inven", NULL);
3747 	Cmd_AddCommand ("kill", NULL);
3748 	Cmd_AddCommand ("use", NULL);
3749 	Cmd_AddCommand ("drop", NULL);
3750 
3751 	//r1: loc support
3752 	Cmd_AddCommand ("say", NULL);
3753 	Cmd_AddCommand ("say_team", NULL);
3754 
3755 	Cmd_AddCommand ("info", NULL);
3756 
3757 	//???
3758 	//Cmd_AddCommand ("prog", NULL);
3759 
3760 	Cmd_AddCommand ("give", NULL);
3761 	Cmd_AddCommand ("god", NULL);
3762 	Cmd_AddCommand ("notarget", NULL);
3763 	Cmd_AddCommand ("noclip", NULL);
3764 	Cmd_AddCommand ("invuse", NULL);
3765 	Cmd_AddCommand ("invprev", NULL);
3766 	Cmd_AddCommand ("invnext", NULL);
3767 	Cmd_AddCommand ("invdrop", NULL);
3768 	Cmd_AddCommand ("weapnext", NULL);
3769 	Cmd_AddCommand ("weapprev", NULL);
3770 }
3771 
3772 
3773 
3774 /*
3775 ===============
3776 CL_WriteConfiguration
3777 
3778 Writes key bindings and archived cvars to config.cfg
3779 ===============
3780 */
CL_WriteConfiguration(void)3781 void CL_WriteConfiguration (void)
3782 {
3783 	FILE	*f;
3784 	char	path[MAX_QPATH];
3785 
3786 	if (cls.state == ca_uninitialized)
3787 		return;
3788 
3789 	Com_sprintf (path, sizeof(path),"%s/config.cfg",FS_Gamedir());
3790 	f = fopen (path, "w");
3791 	if (!f)
3792 	{
3793 		Com_Printf ("Couldn't write config.cfg.\n", LOG_CLIENT);
3794 		return;
3795 	}
3796 
3797 	fprintf (f, "// generated by quake, do not modify\n");
3798 	Key_WriteBindings (f);
3799 	fclose (f);
3800 
3801 	Cvar_WriteVariables (path);
3802 }
3803 
3804 
3805 /*
3806 ==================
3807 CL_FixCvarCheats
3808 
3809 ==================
3810 */
3811 
3812 typedef struct
3813 {
3814 	char	*name;
3815 	float	value;
3816 	float	setval;
3817 	cvar_t	*var;
3818 } cheatvar_t;
3819 
3820 cheatvar_t	cheatvars[] = {
3821 	{"timescale", 1, 1, NULL},
3822 	{"timedemo", 0, 0, NULL},
3823 	{"r_drawworld", 1, 1, NULL},
3824 	{"cl_testlights", 0, 0, NULL},
3825 	{"cl_testblend", 0, 0, NULL},
3826 	{"r_fullbright", 0, 0, NULL},
3827 	{"r_drawflat", 0, 0, NULL},
3828 	{"paused", 0, 0, NULL},
3829 	{"fixedtime", 0, 0, NULL},
3830 	{"sw_draworder", 0, 0, NULL},
3831 	{"gl_lightmap", 0, 0, NULL},
3832 	{"gl_saturatelighting", 0, 0, NULL},
3833 	{"gl_lockpvs", 0, 0, NULL},
3834 	{"sw_lockpvs", 0, 0, NULL},
3835 	{NULL, 0, 0, NULL}
3836 };
3837 
3838 int		numcheatvars;
3839 
_cheatcvar_changed(cvar_t * cvar,char * oldValue,char * newValue)3840 void _cheatcvar_changed (cvar_t *cvar, char *oldValue, char *newValue)
3841 {
3842 	int			i;
3843 	cheatvar_t	*var;
3844 
3845 #ifdef _DEBUG
3846 	return;
3847 #endif
3848 
3849 	if (cls.state == ca_disconnected || cl.attractloop || Com_ServerState() == ss_demo || cl.maxclients == 1)
3850 		return;		// single player can cheat
3851 
3852 	for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
3853 	{
3854 		if (var->var == cvar && var->value != cvar->value)
3855 		{
3856 			Com_Printf ("%s is cheat protected.\n", LOG_GENERAL, var->name);
3857 			Cvar_SetValue (var->name, var->setval);
3858 			return;
3859 		}
3860 	}
3861 }
3862 
CL_FixCvarCheats(void)3863 void CL_FixCvarCheats (void)
3864 {
3865 	int			i;
3866 	cheatvar_t	*var;
3867 
3868 	// find all the cvars if we haven't done it yet
3869 	if (!numcheatvars)
3870 	{
3871 		while (cheatvars[numcheatvars].name)
3872 		{
3873 			cheatvars[numcheatvars].var = Cvar_Get (cheatvars[numcheatvars].name,
3874 					va("%g", cheatvars[numcheatvars].value), 0);
3875 			cheatvars[numcheatvars].var->changed = _cheatcvar_changed;
3876 			numcheatvars++;
3877 		}
3878 	}
3879 
3880 	if (cls.state == ca_disconnected || cl.attractloop || Com_ServerState() == ss_demo || cl.maxclients == 1)
3881 		return;		// single player can cheat
3882 
3883 	// make sure they are all set to the proper values
3884 	for (i=0, var = cheatvars ; i<numcheatvars ; i++, var++)
3885 	{
3886 		if (var->var->value != var->value)
3887 		{
3888 			Cvar_SetValue (var->name, var->setval);
3889 		}
3890 	}
3891 }
3892 
3893 //============================================================================
3894 
3895 
3896 
3897 /*
3898 ==================
3899 CL_Frame
3900 
3901 ==================
3902 */
3903 
3904 #ifdef _WIN32
3905 extern qboolean	ActiveApp;
3906 #endif
3907 
3908 extern cvar_t *cl_lents;
3909 void LE_RunLocalEnts (void);
3910 
3911 //CL_RefreshInputs
3912 //jec - updates all input events
3913 
3914 void IN_Commands (void);
3915 
3916 void CL_RefreshCmd (void);
CL_RefreshInputs(void)3917 void CL_RefreshInputs (void)
3918 {
3919 	// process new key events
3920 	Sys_SendKeyEvents ();
3921 
3922 #if (defined JOYSTICK) || (defined LINUX)
3923 	// process mice & joystick events
3924 	IN_Commands ();
3925 #endif
3926 
3927 	// process console commands
3928 	Cbuf_Execute ();
3929 
3930 	// process packets from server
3931 	CL_ReadPackets();
3932 
3933 	//jec - update usercmd state
3934 	if (cls.state > ca_connecting)
3935 		CL_RefreshCmd();
3936 }
3937 
CL_LoadDeferredModels(void)3938 void CL_LoadDeferredModels (void)
3939 {
3940 	if (deferred_model_index == MAX_MODELS || !cl.refresh_prepped)
3941 		return;
3942 
3943 	for (;;)
3944 	{
3945 		deferred_model_index ++;
3946 
3947 		if (deferred_model_index == MAX_MODELS)
3948 		{
3949 			re.EndRegistration ();
3950 			Com_DPrintf ("CL_LoadDeferredModels: All done.\n");
3951 			return;
3952 		}
3953 
3954 		if (!cl.configstrings[CS_MODELS+deferred_model_index][0])
3955 			continue;
3956 
3957 		if (cl.configstrings[CS_MODELS+deferred_model_index][0] != '#')
3958 		{
3959 			//Com_DPrintf ("CL_LoadDeferredModels: Now loading '%s'...\n", cl.configstrings[CS_MODELS+deferred_model_index]);
3960 			cl.model_draw[deferred_model_index] = re.RegisterModel (cl.configstrings[CS_MODELS+deferred_model_index]);
3961 			if (cl.configstrings[CS_MODELS+deferred_model_index][0] == '*')
3962 				cl.model_clip[deferred_model_index] = CM_InlineModel (cl.configstrings[CS_MODELS+deferred_model_index]);
3963 			else
3964 				cl.model_clip[deferred_model_index] = NULL;
3965 		}
3966 
3967 		break;
3968 	}
3969 }
3970 
CL_SendCommand_Synchronous(void)3971 void CL_SendCommand_Synchronous (void)
3972 {
3973 	// get new key events
3974 	Sys_SendKeyEvents ();
3975 
3976 	// allow mice or other external controllers to add commands
3977 #ifdef JOYSTICK
3978 	IN_Commands ();
3979 #endif
3980 
3981 	// process console commands
3982 	Cbuf_Execute ();
3983 
3984 	// fix any cheating cvars
3985 	//CL_FixCvarCheats ();
3986 
3987 	// send intentions now
3988 	CL_SendCmd_Synchronous ();
3989 
3990 	// resend a connection request if necessary
3991 	CL_CheckForResend ();
3992 }
3993 
CL_Synchronous_Frame(int msec)3994 void CL_Synchronous_Frame (int msec)
3995 {
3996 	static int	extratime;
3997 
3998 	if (dedicated->value)
3999 		return;
4000 
4001 	extratime += msec;
4002 
4003 	if (!cl_timedemo->value)
4004 	{
4005 		if (cls.state == ca_connected)
4006 		{
4007 			if (extratime < 100 && !send_packet_now)
4008 				return;			// don't flood packets out while connecting
4009 		}
4010 		else
4011 		{
4012 			if (extratime < 1000/cl_maxfps->value)
4013 				return;
4014 		}
4015 	}
4016 
4017 	// let the mouse activate or deactivate
4018 	IN_Frame ();
4019 
4020 	// decide the simulation time
4021 	cls.frametime = extratime/1000.0f;
4022 	cl.time += extratime;
4023 	cls.realtime = curtime;
4024 
4025 	extratime = 0;
4026 #if 0
4027 	if (cls.frametime > (1.0f / cl_minfps->value))
4028 		cls.frametime = (1.0f / cl_minfps->value);
4029 #else
4030 	if (cls.frametime > (1.0f / 5))
4031 		cls.frametime = (1.0f / 5);
4032 #endif
4033 
4034 	// if in the debugger last frame, don't timeout
4035 	if (msec > 5000)
4036 		cls.netchan.last_received = Sys_Milliseconds ();
4037 
4038 	send_packet_now = false;
4039 
4040 #ifdef USE_CURL
4041 	CL_RunHTTPDownloads ();
4042 #endif
4043 
4044 	// fetch results from server
4045 	CL_ReadPackets ();
4046 
4047 	// send a new command message to the server
4048 	CL_SendCommand_Synchronous ();
4049 
4050 	// predict all unacknowledged movements
4051 	CL_PredictMovement ();
4052 
4053 	// allow rendering DLL change
4054 	if (vid_ref->modified)
4055 	{
4056 		VID_ReloadRefresh ();
4057 	}
4058 
4059 	if (!cl.refresh_prepped && cls.state == ca_active)
4060 		CL_PrepRefresh ();
4061 
4062 	CL_LoadDeferredModels();
4063 
4064 	// update the screen
4065 	SCR_UpdateScreen ();
4066 
4067 	// update audio
4068 	S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up);
4069 
4070 #ifdef CD_AUDIO
4071 	CDAudio_Update();
4072 #endif
4073 
4074 	if (cls.spamTime && cls.spamTime < cls.realtime)
4075 	{
4076 		Cbuf_AddText ("say \"" PRODUCTNAME " " VERSION " " __TIMESTAMP__ " " CPUSTRING " " BUILDSTRING " [http://r1ch.net/r1q2]\"\n");
4077 		cls.lastSpamTime = cls.realtime;
4078 		cls.spamTime = 0;
4079 	}
4080 
4081 	if (cl_lents->intvalue)
4082 		LE_RunLocalEnts ();
4083 
4084 	// advance local effects for next frame
4085 	CL_RunDLights ();
4086 	CL_RunLightStyles ();
4087 
4088 #ifdef CINEMATICS
4089 	SCR_RunCinematic ();
4090 #endif
4091 
4092 	SCR_RunConsole ();
4093 }
4094 
4095 
4096 //CL_SendCommand
4097 //jec - prepare and send out the current usercmd state.
CL_SendCommand(void)4098 void CL_SendCommand (void)
4099 {
4100 	// fix any cheating cvars
4101 	//CL_FixCvarCheats ();
4102 
4103 	// send client packet to server
4104 	CL_SendCmd ();
4105 
4106 	// resend a connection request if necessary
4107 	CL_CheckForResend ();
4108 }
4109 
CL_Frame(int msec)4110 void CL_Frame (int msec)
4111 {
4112 	//jec - this function's been rearranged a lot,
4113 	//	to decouple various client activities.
4114 	static int	packet_delta,
4115 				render_delta,
4116 				misc_delta = 1000;
4117 
4118 	//static int inputCount = 0;
4119 
4120 	qboolean	packet_frame=true,
4121 				render_frame=true,
4122 				misc_frame=true;
4123 
4124 #ifndef NO_SERVER
4125 	if (dedicated->intvalue)
4126 		return;
4127 #endif
4128 
4129 #ifdef _DEBUG
4130 	//if (!ActiveApp)
4131 		//NET_Client_Sleep (50);
4132 
4133 	if (dbg_framesleep->intvalue)
4134 		Sys_Sleep (dbg_framesleep->intvalue);
4135 #else
4136 #ifdef _WIN32
4137 	if (!ActiveApp && !Com_ServerState())
4138 		NET_Client_Sleep (100);
4139 #endif
4140 #endif
4141 
4142 	if (cl_async->intvalue != 1)
4143 	{
4144 		CL_Synchronous_Frame (msec);
4145 		return;
4146 	}
4147 
4148 	//jec - set internal counters
4149 	packet_delta += msec;
4150 	render_delta += msec;
4151 	misc_delta += msec;
4152 
4153 	//jec - set the frame counters
4154 	cl.time += msec;
4155 	cls.frametime = packet_delta/1000.0f;
4156 	cls.realtime = curtime;
4157 
4158 	//if (cls.frametime > 0.05)
4159 	//	Com_Printf ("Hitch warning: %f (%d ms)\n", LOG_CLIENT, cls.frametime, msec);
4160 
4161 	//if in the debugger last frame, don't timeout
4162 	if (msec > 5000)
4163 		cls.netchan.last_received = Sys_Milliseconds ();
4164 
4165 	// don't extrapolate too far ahead
4166 	if (cls.frametime > .5)
4167 		cls.frametime = .5;
4168 
4169 	//jec - determine what all should be done...
4170 	if (!cl_timedemo->intvalue)
4171 	{
4172 		// packet transmission rate is too high
4173 		if (packet_delta < 1000/cl_maxfps->intvalue)
4174 			packet_frame = false;
4175 
4176 		// don't need to do this stuff much.
4177 		if( misc_delta < 250)
4178 			misc_frame = false;
4179 
4180 		// framerate is too high
4181 		if (render_delta < 1000/r_maxfps->intvalue)
4182 			render_frame = false;
4183 	}
4184 
4185 	// don't flood packets out while connecting
4186 	if (cls.state == ca_connected)
4187 	{
4188 		if (packet_delta < 100)
4189 			packet_frame = false;
4190 
4191 #ifdef USE_CURL
4192 		//we run full speed when connecting
4193 		CL_RunHTTPDownloads ();
4194 #endif
4195 	}
4196 
4197 	//jec - update the inputs (keybd, mouse, server, etc)
4198 	CL_RefreshInputs ();
4199 
4200 	if ((send_packet_now && cl_instantpacket->intvalue) || userinfo_modified)
4201 	{
4202 		if (cls.state < ca_connected)
4203 		{
4204 			userinfo_modified = false;
4205 		}
4206 		else
4207 		{
4208 			packet_frame = true;
4209 		}
4210 	}
4211 
4212 	send_packet_now = false;
4213 
4214 	//jec- send commands to the server
4215 	//if (++inputCount >= cl_snaps->value && packet_frame)
4216 	if (packet_frame)
4217 	{
4218 		packet_delta = 0;
4219 		CL_SendCommand ();
4220 		CL_LoadDeferredModels();
4221 
4222 #ifdef USE_CURL
4223 		//we run less often in game
4224 		CL_RunHTTPDownloads ();
4225 #endif
4226 	}
4227 
4228 	//jec- Render the display
4229 	if(render_frame)
4230 	{
4231 		render_delta = 0;
4232 
4233 		if(misc_frame)
4234 		{
4235 			//static qboolean snapsInitialized = false;
4236 
4237 			misc_delta = 0;
4238 
4239 			if (cls.spamTime && cls.spamTime < cls.realtime)
4240 			{
4241 				char buff[256];
4242 				Com_sprintf (buff, sizeof(buff), "say \"R1Q2 %s %s %s %s [http://r1ch.net/r1q2]\"\n", VERSION,
4243 					__TIMESTAMP__, CPUSTRING, BUILDSTRING
4244 				);
4245 				Cbuf_AddText (buff);
4246 				cls.lastSpamTime = cls.realtime;
4247 				cls.spamTime = 0;
4248 			}
4249 
4250 			//set the mouse on/off
4251 			IN_Frame();
4252 
4253 			if (vid_ref->modified)
4254 			{
4255 				VID_ReloadRefresh ();
4256 			}
4257 		}
4258 
4259 		if (!cl.refresh_prepped && cls.state == ca_active)
4260 			CL_PrepRefresh ();
4261 
4262 		// predict all unacknowledged movements
4263 		CL_PredictMovement ();
4264 
4265 		//r1: run local ent physics/thinking/etc
4266 		if (cl_lents->intvalue)
4267 			LE_RunLocalEnts ();
4268 
4269 		// update the screen
4270 		SCR_UpdateScreen ();
4271 
4272 		// update audio
4273 		S_Update (cl.refdef.vieworg, cl.v_forward, cl.v_right, cl.v_up);
4274 
4275 	#ifdef CD_AUDIO
4276 		if(misc_frame)
4277 			CDAudio_Update();
4278 	#endif
4279 
4280 		// advance local effects for next frame
4281 		CL_RunDLights ();
4282 		CL_RunLightStyles ();
4283 		SCR_RunConsole ();
4284 	}
4285 		//cls.framecount++;
4286 	//}
4287 }
4288 
4289 //============================================================================
4290 
4291 /*
4292 ====================
4293 CL_Init
4294 ====================
4295 */
4296 void LE_Init (void);
CL_Init(void)4297 void CL_Init (void)
4298 {
4299 	int i;
4300 
4301 #ifndef NO_SERVER
4302 	if (dedicated->intvalue)
4303 		return;		// nothing running on the client
4304 #endif
4305 
4306 	//r1: init string table
4307 	for (i = svc_max_enttypes; i < 256; i++)
4308 		svc_strings[i] = svc_strings[0];
4309 
4310 	// all archived variables will now be loaded
4311 
4312 //	Cbuf_AddText ("exec autoexec.cfg\n");
4313 	FS_ExecConfig ("autoexec.cfg");
4314 	Cbuf_Execute ();
4315 
4316 	Con_Init ();
4317 #if defined __linux__ || defined __sgi || defined __FreeBSD__
4318 	S_Init (true);
4319 
4320 	VID_Init ();
4321 	//r1: after initing video new console size is probably in use... lets use it for remaining
4322 	//messages
4323 	Con_CheckResize();
4324 #else
4325 	VID_Init ();
4326 
4327 	//r1: after initing video new console size is probably in use... lets use it for remaining
4328 	//messages
4329 	Con_CheckResize();
4330 
4331 	S_Init (true);	// sound must be initialized after window is created
4332 #endif
4333 
4334 	V_Init ();
4335 
4336 #ifdef CLIENT_DLL
4337 	CL_ClDLL_Restart_f ();
4338 #endif
4339 
4340 	SZ_Init (&net_message, net_message_buffer, sizeof(net_message_buffer));
4341 
4342 	M_Init ();
4343 
4344 	SCR_Init ();
4345 	//cls.disable_screen = 0;	// don't draw yet
4346 
4347 #ifdef CD_AUDIO
4348 	CDAudio_Init ();
4349 #endif
4350 	CL_InitLocal ();
4351 	IN_Init ();
4352 
4353 	LE_Init ();
4354 
4355 	CL_Loc_Init ();
4356 
4357 #ifdef USE_CURL
4358 	CL_InitHTTPDownloads ();
4359 #endif
4360 
4361 	FS_ExecConfig ("postinit.cfg");
4362 	Cbuf_Execute ();
4363 }
4364 
4365 
4366 /*
4367 ===============
4368 CL_Shutdown
4369 
4370 FIXME: this is a callback from Sys_Quit and Com_Error.  It would be better
4371 to run quit through here before the final handoff to the sys code.
4372 ===============
4373 */
CL_Shutdown(void)4374 void CL_Shutdown(void)
4375 {
4376 	static qboolean isdown = false;
4377 
4378 	if (isdown)
4379 	{
4380 		//printf ("recursive shutdown\n");
4381 		return;
4382 	}
4383 	isdown = true;
4384 
4385 #ifdef USE_CURL
4386 	CL_HTTP_Cleanup (true);
4387 #endif
4388 
4389 	CL_FreeLocs ();
4390 	CL_WriteConfiguration ();
4391 
4392 #ifdef CD_AUDIO
4393 	CDAudio_Shutdown ();
4394 #endif
4395 	S_Shutdown();
4396 	IN_Shutdown ();
4397 	VID_Shutdown();
4398 }
4399