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.input.c  -- builds an intended movement command to send to the server
21 
22 #include "client.h"
23 #include "vid_public.h"
24 
25 static cvar_t	*cl_nodelta;
26 static cvar_t	*cl_maxpackets;
27 static cvar_t	*cl_packetdup;
28 static cvar_t	*cl_fuzzhack;
29 static cvar_t	*cl_showpackets;
30 static cvar_t	*cl_instantpacket;
31 
32 static cvar_t	*cl_mouseFilter;
33 static cvar_t	*cl_mouseAccel;
34 
35 cvar_t	*cl_upspeed;
36 cvar_t	*cl_forwardspeed;
37 cvar_t	*cl_sidespeed;
38 cvar_t	*cl_yawspeed;
39 cvar_t	*cl_pitchspeed;
40 cvar_t	*cl_run;
41 cvar_t	*cl_anglespeedkey;
42 
43 static qboolean mlooking;
44 
45 static uint32	frame_msec;
46 static uint32	old_eventTime;
47 
48 inputAPI_t	input;
49 
50 void CL_CenterView (void);
51 
52 /*
53 ===============================================================================
54 
55 INPUT LIBRARY
56 
57 ===============================================================================
58 */
59 
60 static cvar_t	*in_driver;
61 static cvar_t	*in_enable;
62 
63 #define DEFAULT_INPUT_DRIVER	""
64 
65 typedef struct inputDriver_s {
66 	char *name;
67 	void	(*FillAPI)( inputAPI_t *api );
68 } inputDriver_t;
69 
70 #ifdef USE_DINPUT
71 void DI_FillAPI( inputAPI_t *api );
72 #endif
73 
74 #ifdef __linux__
75 void Evdev_FillAPI( inputAPI_t *api );
76 #endif
77 
78 static inputDriver_t	in_driverTable[] = {
79 	/* fallback driver should be present on all systems */
80 	{ "video", Video_FillInputAPI },
81 
82 	/* DirectInput driver */
83 #ifdef USE_DINPUT
84 	{ "dinput", DI_FillAPI },
85 #endif
86 #ifdef __linux__
87 	{ "evdev", Evdev_FillAPI },
88 #endif
89 };
90 
91 static int in_numDrivers = sizeof( in_driverTable ) / sizeof( in_driverTable[0] );
92 
93 /*
94 ============
95 CL_InitInput
96 ============
97 */
CL_InputActivate(qboolean active)98 void CL_InputActivate( qboolean active ) {
99 	if( !cls.input_initialized ) {
100 		return;
101 	}
102 
103 	if( active && ( cls.key_dest & KEY_CONSOLE ) ) {
104 		/* never activate in windowed mode if console is open */
105 		if( !Cvar_VariableInteger( "vid_fullscreen" ) ) {
106 			return;
107 		}
108 	}
109 
110 	input.ClearStates();
111 	input.Activate( active );
112 }
113 
114 /*
115 ============
116 CL_InitInput
117 ============
118 */
CL_InputFrame(void)119 void CL_InputFrame( void ) {
120 	if( !cls.input_initialized ) {
121 		return;
122 	}
123 
124 	input.Frame();
125 }
126 
127 /*
128 ============
129 CL_RestartInput_f
130 ============
131 */
CL_RestartInput_f(void)132 static void CL_RestartInput_f( void ) {
133 	CL_ShutdownInput();
134 	CL_InitInput();
135 }
136 
137 /*
138 ============
139 CL_ShutdownInput
140 ============
141 */
CL_ShutdownInput(void)142 void CL_ShutdownInput( void ) {
143 	if( !cls.input_initialized ) {
144 		return;
145 	}
146 
147 	Cmd_RemoveCommand( "in_restart" );
148 
149 	input.Shutdown();
150 	cls.input_initialized = qfalse;
151 }
152 
153 /*
154 ============
155 CL_InitInput
156 ============
157 */
CL_InitInput(void)158 void CL_InitInput( void ) {
159 	inputDriver_t *driver;
160 	int i;
161 
162 	in_driver = Cvar_Get( "in_driver", "", CVAR_LATCHED );
163 	in_driver->subsystem = CVAR_SYSTEM_INPUT;
164 	in_enable = Cvar_Get( "in_enable", "1", CVAR_LATCHED );
165 	in_enable->subsystem = CVAR_SYSTEM_INPUT;
166 
167 	Cmd_AddCommand( "in_restart", CL_RestartInput_f );
168 
169 	if( !in_enable->integer ) {
170 		Com_Printf( "Non-keyboard input disabled\n" );
171 		return;
172 	}
173 
174 	while( 1 ) {
175 		if( in_driver->string[0] ) {
176 			for( i = 0, driver = in_driverTable; i < in_numDrivers; i++, driver++ ) {
177 				if( !strcmp( in_driver->string, driver->name ) ) {
178 					break;
179 				}
180 			}
181 			if( i == in_numDrivers ) {
182 				Com_Printf( "Input driver '%s' not found, falling back to default...\n", in_driver->string );
183 				Cvar_Set( "in_driver", "" );
184 				driver = &in_driverTable[0];
185 			}
186 		} else {
187 			driver = &in_driverTable[0];
188 		}
189 
190 		driver->FillAPI( &input );
191 
192 		if( input.Init() ) {
193 			break;
194 		}
195 		if( !in_driver->string[0] ) {
196 			Com_WPrintf( "Couldn't fall back to default input driver!\n"
197 				"Non-keyboard input disabled\n" );
198 			return;
199 		}
200 
201 		Com_Printf( "Failed to load input driver, falling back to default...\n" );
202 		Cvar_Set( "in_driver", "" );
203 	}
204 
205 	cls.input_initialized = qtrue;
206 
207 	CL_InputActivate( cls.appactive );
208 
209 }
210 
211 
212 /*
213 ===============================================================================
214 
215 KEY BUTTONS
216 
217 Continuous button event tracking is complicated by the fact that two different
218 input sources (say, mouse button 1 and the control key) can both press the
219 same button, but the button should only be released when both of the
220 pressing key have been released.
221 
222 When a key event issues a button command (+forward, +attack, etc), it appends
223 its key number as a parameter to the command so it can be matched up with
224 the release.
225 
226 state bit 0 is the current state of the key
227 state bit 1 is edge triggered on the up to down transition
228 state bit 2 is edge triggered on the down to up transition
229 
230 
231 Key_Event (int key, qboolean down, unsigned time);
232 
233   +mlook src time
234 
235 ===============================================================================
236 */
237 
238 typedef struct kbutton_s {
239 	int			down[2];		// key nums holding it down
240 	uint32		downtime;		// msec timestamp
241 	uint32		msec;			// msec down this frame
242 	int			state;
243 } kbutton_t;
244 
245 static kbutton_t	in_klook;
246 static kbutton_t	in_left, in_right, in_forward, in_back;
247 static kbutton_t	in_lookup, in_lookdown, in_moveleft, in_moveright;
248 static kbutton_t	in_strafe, in_speed, in_use, in_attack;
249 static kbutton_t	in_up, in_down;
250 
251 static int			in_impulse;
252 
253 
KeyDown(kbutton_t * b)254 static void KeyDown (kbutton_t *b)
255 {
256 	int		k;
257 	char	*c;
258 
259 	c = Cmd_Argv(1);
260 	if (c[0])
261 		k = atoi(c);
262 	else
263 		k = -1;		// typed manually at the console for continuous down
264 
265 	if (k == b->down[0] || k == b->down[1])
266 		return;		// repeating key
267 
268 	if (!b->down[0])
269 		b->down[0] = k;
270 	else if (!b->down[1])
271 		b->down[1] = k;
272 	else
273 	{
274 		Com_WPrintf ("Three keys down for a button!\n");
275 		return;
276 	}
277 
278 	if (b->state & 1)
279 		return;		// still down
280 
281 	// save timestamp
282 	c = Cmd_Argv(2);
283 	b->downtime = atoi(c);
284 	if (!b->downtime) {
285 		b->downtime = com_eventTime - 100;
286 	}
287 
288 	b->state |= 1 + 2;	// down + impulse down
289 }
290 
KeyUp(kbutton_t * b)291 static void KeyUp (kbutton_t *b)
292 {
293 	int		k;
294 	char	*c;
295 	uint32	uptime;
296 
297 	c = Cmd_Argv(1);
298 	if (c[0])
299 		k = atoi(c);
300 	else
301 	{ // typed manually at the console, assume for unsticking, so clear all
302 		b->down[0] = b->down[1] = 0;
303 		b->state = 0;	// impulse up
304 		return;
305 	}
306 
307 	if (b->down[0] == k)
308 		b->down[0] = 0;
309 	else if (b->down[1] == k)
310 		b->down[1] = 0;
311 	else
312 		return;		// key up without coresponding down (menu pass through)
313 	if (b->down[0] || b->down[1])
314 		return;		// some other key is still holding it down
315 
316 	if (!(b->state & 1))
317 		return;		// still up (this should not happen)
318 
319 	// save timestamp
320 	c = Cmd_Argv(2);
321 	uptime = atoi(c);
322 	if( !uptime ) {
323 		b->msec += 10;
324 	} else if( uptime > b->downtime ) {
325 		b->msec += uptime - b->downtime;
326 	}
327 
328 	b->state = 0;		// now up
329 	//b->state |= 4; 		// impulse up
330 }
331 
IN_KLookDown(void)332 static void IN_KLookDown( void ) { KeyDown( &in_klook ); }
IN_KLookUp(void)333 static void IN_KLookUp( void ) { KeyUp( &in_klook ); }
IN_UpDown(void)334 static void IN_UpDown( void ) { KeyDown( &in_up ); }
IN_UpUp(void)335 static void IN_UpUp( void ) { KeyUp( &in_up ); }
IN_DownDown(void)336 static void IN_DownDown( void ) { KeyDown( &in_down ); }
IN_DownUp(void)337 static void IN_DownUp( void ) { KeyUp( &in_down ); }
IN_LeftDown(void)338 static void IN_LeftDown( void ) { KeyDown( &in_left ); }
IN_LeftUp(void)339 static void IN_LeftUp( void ) { KeyUp( &in_left ); }
IN_RightDown(void)340 static void IN_RightDown( void ) { KeyDown( &in_right ); }
IN_RightUp(void)341 static void IN_RightUp( void ) { KeyUp( &in_right ); }
IN_ForwardDown(void)342 static void IN_ForwardDown( void ) { KeyDown( &in_forward ); }
IN_ForwardUp(void)343 static void IN_ForwardUp( void ) { KeyUp( &in_forward ); }
IN_BackDown(void)344 static void IN_BackDown( void ) { KeyDown( &in_back ); }
IN_BackUp(void)345 static void IN_BackUp( void ) { KeyUp( &in_back ); }
IN_LookupDown(void)346 static void IN_LookupDown( void ) { KeyDown( &in_lookup ); }
IN_LookupUp(void)347 static void IN_LookupUp( void ) { KeyUp( &in_lookup ); }
IN_LookdownDown(void)348 static void IN_LookdownDown( void ) { KeyDown( &in_lookdown ); }
IN_LookdownUp(void)349 static void IN_LookdownUp( void ) { KeyUp( &in_lookdown ); }
IN_MoveleftDown(void)350 static void IN_MoveleftDown( void ) { KeyDown( &in_moveleft ); }
IN_MoveleftUp(void)351 static void IN_MoveleftUp( void ) { KeyUp( &in_moveleft ); }
IN_MoverightDown(void)352 static void IN_MoverightDown( void ) { KeyDown( &in_moveright ); }
IN_MoverightUp(void)353 static void IN_MoverightUp( void ) { KeyUp( &in_moveright ); }
IN_SpeedDown(void)354 static void IN_SpeedDown( void ) { KeyDown( &in_speed ); }
IN_SpeedUp(void)355 static void IN_SpeedUp( void ) { KeyUp( &in_speed ); }
IN_StrafeDown(void)356 static void IN_StrafeDown( void ) { KeyDown( &in_strafe ); }
IN_StrafeUp(void)357 static void IN_StrafeUp( void ) { KeyUp( &in_strafe ); }
IN_AttackDown(void)358 static void IN_AttackDown( void ) { KeyDown( &in_attack ); }
IN_AttackUp(void)359 static void IN_AttackUp( void ) { KeyUp( &in_attack ); }
IN_UseDown(void)360 static void IN_UseDown ( void ) { KeyDown( &in_use ); }
IN_UseUp(void)361 static void IN_UseUp ( void ) { KeyUp( &in_use ); }
362 
IN_Impulse(void)363 static void IN_Impulse ( void ) {
364 	in_impulse = atoi( Cmd_Argv( 1 ) );
365 }
366 
IN_MLookDown(void)367 static void IN_MLookDown( void ) {
368 	mlooking = qtrue;
369 }
370 
IN_MLookUp(void)371 static void IN_MLookUp( void ) {
372 	mlooking = qfalse;
373 
374 	if( !freelook->integer && lookspring->integer )
375 		CL_CenterView();
376 }
377 
378 /*
379 ===============
380 CL_KeyState
381 
382 Returns the fraction of the frame that the key was down
383 ===============
384 */
CL_KeyState(kbutton_t * key)385 float CL_KeyState( kbutton_t *key ) {
386 	float		val;
387 	uint32		msec;
388 
389 	key->state &= ~2;		// clear impulses
390 
391 	msec = key->msec;
392 	key->msec = 0;
393 
394 	if( key->state ) {
395 		// still down
396 		if( com_eventTime > key->downtime ) {
397 			msec += com_eventTime - key->downtime;
398 		}
399 		key->downtime = com_eventTime;
400 	}
401 
402 	if( !frame_msec ) {
403 		return 0;
404 	}
405 	val = ( float )msec / frame_msec;
406 	//if( key == &in_forward ) {
407 	//	if( val ) Com_Printf( "[%d] %u / %u = %.3f\n", key->state, msec, frame_msec, val );
408 	//}
409 
410 	clamp( val, 0, 1 );
411 
412 	return val;
413 }
414 
415 // FIXME: always discrete?
CL_ImmKeyState(kbutton_t * key)416 float CL_ImmKeyState( kbutton_t *key ) {
417 	if( key->state ) {
418 		return 1;
419 	}
420 
421 	return 0;
422 }
423 
424 
425 //==========================================================================
426 
427 static int mouse_x;
428 static int mouse_y;
429 static int old_mouse_x;
430 static int old_mouse_y;
431 
432 /*
433 ================
434 CL_MouseEvent
435 ================
436 */
CL_MouseEvent(int dx,int dy)437 void CL_MouseEvent( int dx, int dy ) {
438 	if( cls.key_dest & KEY_MENU ) {
439 		UI_MouseMove( dx, dy );
440 		return;
441 	}
442 
443 	mouse_x += dx;
444 	mouse_y += dy;
445 }
446 
447 /*
448 ================
449 CL_MouseMove
450 ================
451 */
CL_MouseMove(void)452 static void CL_MouseMove( void ) {
453 	float		mx, my;
454 	float	speed;
455 
456 	if( cl_mouseFilter->integer ) {
457 		mx = ( mouse_x + old_mouse_x ) * 0.5f;
458 		my = ( mouse_y + old_mouse_y ) * 0.5f;
459 	} else {
460 		mx = mouse_x;
461 		my = mouse_y;
462 	}
463 
464 	old_mouse_x = mouse_x;
465 	old_mouse_y = mouse_y;
466 	mouse_x = 0;
467 	mouse_y = 0;
468 
469 	if( !mx && !my ) {
470 		return;
471 	}
472 
473 	Cvar_ClampValue( cl_mouseAccel, 0, 1 );
474 
475 	speed = sqrt( mx * mx + my * my );
476 	speed = sensitivity->value + speed * cl_mouseAccel->value;
477 
478 	mx *= speed;
479 	my *= speed;
480 
481 // add mouse X/Y movement to cmd
482 	if( ( in_strafe.state & 1 ) || ( lookstrafe->value && mlooking ) ) {
483 		cl.cmd.sidemove += m_side->value * mx;
484 	} else {
485 		cl.viewangles[YAW] -= m_yaw->value * mx;
486 	}
487 
488 	if( ( mlooking || freelook->integer ) && !( in_strafe.state & 1 ) ) {
489 		cl.viewangles[PITCH] += m_pitch->value * my;
490 	} else {
491 		cl.cmd.forwardmove -= m_forward->value * my;
492 	}
493 
494 }
495 
496 
497 /*
498 ================
499 CL_AdjustAngles
500 
501 Moves the local angle positions
502 ================
503 */
CL_AdjustAngles(void)504 static void CL_AdjustAngles (void)
505 {
506 	float	speed;
507 	float	up, down;
508 
509 	if (in_speed.state & 1)
510 		speed = cls.frametime * cl_anglespeedkey->value;
511 	else
512 		speed = cls.frametime*0.001f;
513 
514 	if (!(in_strafe.state & 1))
515 	{
516 		cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_ImmKeyState (&in_right);
517 		cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_ImmKeyState (&in_left);
518 	}
519 	if (in_klook.state & 1)
520 	{
521 		cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_ImmKeyState (&in_forward);
522 		cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_ImmKeyState (&in_back);
523 	}
524 
525 	up = CL_ImmKeyState (&in_lookup);
526 	down = CL_ImmKeyState(&in_lookdown);
527 
528 	cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * up;
529 	cl.viewangles[PITCH] += speed*cl_pitchspeed->value * down;
530 }
531 
532 /*
533 ================
534 CL_BaseMove
535 
536 Send the intended movement message to the server
537 ================
538 */
CL_BaseMove(void)539 static void CL_BaseMove( void ) {
540 	vec3_t move;
541 
542 	VectorClear( move );
543 	if( in_strafe.state & 1 ) {
544 		move[1] += cl_sidespeed->value * CL_KeyState( &in_right );
545 		move[1] -= cl_sidespeed->value * CL_KeyState( &in_left );
546 	}
547 
548 	move[1] += cl_sidespeed->value * CL_KeyState( &in_moveright );
549 	move[1] -= cl_sidespeed->value * CL_KeyState( &in_moveleft );
550 
551 	move[2] += cl_upspeed->value * CL_KeyState( &in_up );
552 	move[2] -= cl_upspeed->value * CL_KeyState( &in_down );
553 
554 	if( !( in_klook.state & 1 ) ) {
555 		move[0] += cl_forwardspeed->value * CL_KeyState( &in_forward );
556 		move[0] -= cl_forwardspeed->value * CL_KeyState( &in_back );
557 	}
558 
559 //
560 // adjust for speed key / running
561 //
562 	if( ( in_speed.state & 1 ) ^ cl_run->integer ) {
563 		VectorScale( move, 2, move );
564 	}
565 
566 	cl.cmd.forwardmove = move[0];
567 	cl.cmd.sidemove = move[1];
568 	cl.cmd.upmove = move[2];
569 }
570 
571 /*
572 ================
573 CL_ImmBaseMove
574 
575 Builds intended movement message for
576 local pmove sampling
577 ================
578 */
CL_ImmBaseMove(void)579 static void CL_ImmBaseMove( void ) {
580 	VectorClear( cl.move );
581 	if( in_strafe.state & 1 ) {
582 		cl.move[1] += cl_sidespeed->value * CL_ImmKeyState( &in_right );
583 		cl.move[1] -= cl_sidespeed->value * CL_ImmKeyState( &in_left );
584 	}
585 
586 	cl.move[1] += cl_sidespeed->value * CL_ImmKeyState( &in_moveright );
587 	cl.move[1] -= cl_sidespeed->value * CL_ImmKeyState( &in_moveleft );
588 
589 	cl.move[2] += cl_upspeed->value * CL_ImmKeyState( &in_up );
590 	cl.move[2] -= cl_upspeed->value * CL_ImmKeyState( &in_down );
591 
592 	if( !( in_klook.state & 1 ) ) {
593 		cl.move[0] += cl_forwardspeed->value * CL_ImmKeyState( &in_forward );
594 		cl.move[0] -= cl_forwardspeed->value * CL_ImmKeyState( &in_back );
595 	}
596 
597 //
598 // adjust for speed key / running
599 //
600 	if( ( in_speed.state & 1 ) ^ cl_run->integer ) {
601 		VectorScale( cl.move, 2, cl.move );
602 	}
603 }
604 
CL_ClampPitch(void)605 static void CL_ClampPitch( void ) {
606 	float	pitch;
607 
608 	pitch = SHORT2ANGLE( cl.frame.ps.ps.pmove.delta_angles[PITCH] );
609 	if (pitch > 180)
610 		pitch -= 360;
611 
612 	if (cl.viewangles[PITCH] + pitch < -360)
613 		cl.viewangles[PITCH] += 360; // wrapped
614 	if (cl.viewangles[PITCH] + pitch > 360)
615 		cl.viewangles[PITCH] -= 360; // wrapped
616 
617 	if (cl.viewangles[PITCH] + pitch > 89)
618 		cl.viewangles[PITCH] = 89 - pitch;
619 	if (cl.viewangles[PITCH] + pitch < -89)
620 		cl.viewangles[PITCH] = -89 - pitch;
621 }
622 
623 /*
624 =================
625 CL_UpdateCmd
626 =================
627 */
CL_UpdateCmd(int msec)628 void CL_UpdateCmd( int msec ) {
629 	// adjust viewangles
630 	CL_AdjustAngles();
631 
632 	// get basic movement from keyboard
633 	CL_ImmBaseMove();
634 
635 	// allow mice or other external controllers to add to the move
636 	CL_MouseMove();
637 
638 	// send milliseconds of time to apply the move
639 	cl.cmd.msec += msec;
640 
641 	CL_ClampPitch();
642 
643 	cl.cmd.angles[0] = ANGLE2SHORT( cl.viewangles[0] );
644 	cl.cmd.angles[1] = ANGLE2SHORT( cl.viewangles[1] );
645 	cl.cmd.angles[2] = ANGLE2SHORT( cl.viewangles[2] );
646 }
647 
648 
CL_CenterView(void)649 void CL_CenterView( void ) {
650 	cl.viewangles[PITCH] = -SHORT2ANGLE( cl.frame.ps.ps.pmove.delta_angles[PITCH] );
651 }
652 
653 /*
654 ============
655 CL_RegisterInput
656 ============
657 */
CL_RegisterInput(void)658 void CL_RegisterInput( void ) {
659 	Cmd_AddCommand ("centerview",CL_CenterView);
660 
661 	Cmd_AddCommand ("+moveup",IN_UpDown);
662 	Cmd_AddCommand ("-moveup",IN_UpUp);
663 	Cmd_AddCommand ("+movedown",IN_DownDown);
664 	Cmd_AddCommand ("-movedown",IN_DownUp);
665 	Cmd_AddCommand ("+left",IN_LeftDown);
666 	Cmd_AddCommand ("-left",IN_LeftUp);
667 	Cmd_AddCommand ("+right",IN_RightDown);
668 	Cmd_AddCommand ("-right",IN_RightUp);
669 	Cmd_AddCommand ("+forward",IN_ForwardDown);
670 	Cmd_AddCommand ("-forward",IN_ForwardUp);
671 	Cmd_AddCommand ("+back",IN_BackDown);
672 	Cmd_AddCommand ("-back",IN_BackUp);
673 	Cmd_AddCommand ("+lookup", IN_LookupDown);
674 	Cmd_AddCommand ("-lookup", IN_LookupUp);
675 	Cmd_AddCommand ("+lookdown", IN_LookdownDown);
676 	Cmd_AddCommand ("-lookdown", IN_LookdownUp);
677 	Cmd_AddCommand ("+strafe", IN_StrafeDown);
678 	Cmd_AddCommand ("-strafe", IN_StrafeUp);
679 	Cmd_AddCommand ("+moveleft", IN_MoveleftDown);
680 	Cmd_AddCommand ("-moveleft", IN_MoveleftUp);
681 	Cmd_AddCommand ("+moveright", IN_MoverightDown);
682 	Cmd_AddCommand ("-moveright", IN_MoverightUp);
683 	Cmd_AddCommand ("+speed", IN_SpeedDown);
684 	Cmd_AddCommand ("-speed", IN_SpeedUp);
685 	Cmd_AddCommand ("+attack", IN_AttackDown);
686 	Cmd_AddCommand ("-attack", IN_AttackUp);
687 	Cmd_AddCommand ("+use", IN_UseDown);
688 	Cmd_AddCommand ("-use", IN_UseUp);
689 	Cmd_AddCommand ("impulse", IN_Impulse);
690 	Cmd_AddCommand ("+klook", IN_KLookDown);
691 	Cmd_AddCommand ("-klook", IN_KLookUp);
692 	Cmd_AddCommand( "+mlook", IN_MLookDown );
693 	Cmd_AddCommand( "-mlook", IN_MLookUp );
694 
695 	cl_nodelta = Cvar_Get ("cl_nodelta", "0", 0);
696 	cl_maxpackets = Cvar_Get( "cl_maxpackets", "30", 0 );
697 	cl_fuzzhack = Cvar_Get( "cl_fuzzhack", "1", 0 );
698 	cl_packetdup = Cvar_Get( "cl_packetdup", "1", 0 );
699 	cl_showpackets = Cvar_Get( "cl_showpackets", "0", 0 );
700 	cl_instantpacket = Cvar_Get( "cl_instantpacket", "0", 0 );
701 
702 	cl_mouseFilter = Cvar_Get( "m_filter", "0", 0 );
703 	cl_mouseAccel = Cvar_Get( "m_accel", "0", 0 );
704 }
705 
CL_FinalizeCmd(void)706 void CL_FinalizeCmd( void ) {
707 	// this is the only place where console commands are processed.
708 	Cbuf_Execute();
709 
710 	if( cls.state < ca_active ) {
711 		return; // not talking to a server
712 	}
713 
714 	if( sv_paused->integer ) {
715 		return;
716 	}
717 
718 	frame_msec = com_eventTime - old_eventTime;
719 	clamp( frame_msec, 1, 200 );
720 
721 //
722 // figure button bits
723 //
724 	if ( in_attack.state & 3 )
725 		cl.cmd.buttons |= BUTTON_ATTACK;
726 	in_attack.state &= ~2;
727 
728 	if (in_use.state & 3)
729 		cl.cmd.buttons |= BUTTON_USE;
730 	in_use.state &= ~2;
731 
732 	if( !( cls.key_dest & (KEY_CONSOLE|KEY_MENU|KEY_MESSAGE) ) && Key_AnyKeyDown() )
733 		cl.cmd.buttons |= BUTTON_ANY;
734 
735 	if( cl.cmd.msec > 250 ) {
736 		cl.cmd.msec = 100;		// time was unreasonable
737 	}
738 
739 	CL_BaseMove();
740 
741 	cl.cmd.impulse = in_impulse;
742 	in_impulse = 0;
743 
744 	// save this command off for prediction
745 	cl.cmdNumber++;
746 	cl.cmds[cl.cmdNumber & CMD_MASK] = cl.cmd;
747 
748 	// clear pending cmd
749 	memset( &cl.cmd, 0, sizeof( cl.cmd ) );
750 
751 	if( cl.cmd.buttons && cl.cinematictime > 0 && !cl.attractLoop
752 		&& cls.realtime - cl.cinematictime > 1000 )
753 	{	// skip the rest of the cinematic
754 		SCR_FinishCinematic ();
755 	}
756 
757 	old_eventTime = com_eventTime;
758 }
759 
760 
761 /*
762 =================
763 CL_ReadyToSend
764 =================
765 */
CL_ReadyToSend(void)766 static qboolean CL_ReadyToSend( void ) {
767 	int msec;
768 
769 	if( cl_instantpacket->integer && ( ( in_attack.state & 2 ) || ( in_use.state & 2 ) ) ) {
770 		return qtrue;
771 	}
772 	if( cls.netchan->message.cursize || cls.netchan->reliable_ack_pending ) {
773 		return qtrue;
774 	}
775 	if( cls.serverProtocol != PROTOCOL_VERSION_Q2PRO && !cl_fuzzhack->integer ) {
776 		return qtrue;
777 	}
778 	if( !cl_maxpackets->integer ) {
779 		return qtrue;
780 	}
781 
782 	if( cl_maxpackets->integer < 10 ) {
783 		Cvar_SetInteger( "cl_maxpackets", 10 );
784 	}
785 	if( cl.lastTransmitTime > cls.realtime ) {
786 		cl.lastTransmitTime = cls.realtime;
787 	}
788 
789 	msec = 1000 / cl_maxpackets->integer;
790 	if( msec ) {
791 		msec = 100 / ( 100 / msec );
792 	}
793 	//Com_Printf( "%d\n", msec );
794 	if( cls.realtime - cl.lastTransmitTime < msec ) {
795 		return qfalse;
796 	}
797 
798 	return qtrue;
799 
800 }
801 
802 /*
803 =================
804 CL_SendDefaultCmd
805 =================
806 */
CL_SendDefaultCmd(void)807 static void CL_SendDefaultCmd( void ) {
808 	int			i;
809 	int			checksumIndex;
810 	usercmd_t	*cmd, *oldcmd;
811 	client_history_t	*history;
812 
813 	// archive this packet
814 	history = &cl.history[cls.netchan->outgoing_sequence & CMD_MASK];
815 	history->cmdNumber = cl.cmdNumber;
816 	history->realtime = cls.realtime;	// for ping calculation
817 
818 	cl.lastTransmitCmdNumber = cl.cmdNumber;
819 
820 	// see if we are ready to send this packet
821 	if( !CL_ReadyToSend() ) {
822 		if( cl.cmdNumber - cl.lastTransmitCmdNumber < 3 ) {
823 			cls.netchan->outgoing_sequence++; // HACK: just drop the packet
824 			return;
825 		}
826 	}
827 
828 	cl.lastTransmitTime = cls.realtime;
829 
830 	// begin a client move command
831 	MSG_WriteByte( clc_move );
832 
833 	// save the position for a checksum byte
834 	checksumIndex = 0;
835 	if( cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT ) {
836 		checksumIndex = msg_write.cursize;
837 		SZ_GetSpace( &msg_write, 1 );
838 	}
839 
840 	// let the server know what the last frame we
841 	// got was, so the next message can be delta compressed
842 	if( cl_nodelta->integer || !cl.frame.valid || cls.demowaiting ) {
843 		MSG_WriteLong( -1 ); // no compression
844 	} else {
845 		MSG_WriteLong( cl.frame.serverFrame );
846 	}
847 
848 	// send this and the previous cmds in the message, so
849 	// if the last packet was dropped, it can be recovered
850 	cmd = &cl.cmds[( cl.cmdNumber - 2 ) & CMD_MASK];
851 	MSG_WriteDeltaUsercmd( NULL, cmd );
852 	MSG_WriteByte( cl.lightlevel );
853 	oldcmd = cmd;
854 
855 	cmd = &cl.cmds[( cl.cmdNumber - 1 ) & CMD_MASK];
856 	MSG_WriteDeltaUsercmd( oldcmd, cmd );
857 	MSG_WriteByte( cl.lightlevel );
858 	oldcmd = cmd;
859 
860 	cmd = &cl.cmds[cl.cmdNumber & CMD_MASK];
861 	MSG_WriteDeltaUsercmd( oldcmd, cmd );
862 	MSG_WriteByte( cl.lightlevel );
863 
864 	if( cls.serverProtocol <= PROTOCOL_VERSION_DEFAULT ) {
865 		// calculate a checksum over the move commands
866 		msg_write.data[checksumIndex] = COM_BlockSequenceCRCByte(
867 			msg_write.data + checksumIndex + 1, msg_write.cursize - checksumIndex - 1,
868 			cls.netchan->outgoing_sequence );
869 	}
870 
871 	//
872 	// deliver the message
873 	//
874 	i = cls.netchan->Transmit( cls.netchan, msg_write.cursize, msg_write.data );
875 	if( i == -1 ) {
876 		Com_Error( ERR_DISCONNECT, "Connection reset by peer" );
877 	}
878 
879 	SCR_AddLagometerOutPacketInfo( i );
880 
881 	if( cl_showpackets->integer ) {
882 		Com_Printf( "%i ", i );
883 	}
884 
885 	SZ_Clear( &msg_write );
886 }
887 
888 /*
889 =================
890 CL_SendBatchedCmd
891 =================
892 */
CL_SendBatchedCmd(void)893 static void CL_SendBatchedCmd( void ) {
894 	int			i, j, seq, bits;
895 	int			numCmds, numDups;
896 	int			totalCmds, totalMsec;
897 	usercmd_t	*cmd, *oldcmd;
898 	client_history_t	*history, *oldest;
899 	byte *patch;
900 
901 	// see if we are ready to send this packet
902 	if( !CL_ReadyToSend() ) {
903 		return;
904 	}
905 
906 	// archive this packet
907 	seq = cls.netchan->outgoing_sequence;
908 	history = &cl.history[seq & CMD_MASK];
909 	history->cmdNumber = cl.cmdNumber;
910 	history->realtime = cls.realtime;	// for ping calculation
911 
912 	cl.lastTransmitTime = cls.realtime;
913 	cl.lastTransmitCmdNumber = cl.cmdNumber;
914 
915 	// begin a client move command
916 	patch = SZ_GetSpace( &msg_write, 1 );
917 
918 	// let the server know what the last frame we
919 	// got was, so the next message can be delta compressed
920 	if( cl_nodelta->integer || !cl.frame.valid || cls.demowaiting ) {
921 		*patch = clc_move_nodelta; // no compression
922 	} else {
923 		*patch = clc_move_batched;
924 		MSG_WriteLong( cl.frame.serverFrame );
925 	}
926 
927 	Cvar_ClampInteger( cl_packetdup, 0, MAX_PACKET_FRAMES - 1 );
928 	numDups = cl_packetdup->integer;
929 
930 	*patch |= numDups << SVCMD_BITS;
931 
932 	// send lightlevel
933 	MSG_WriteByte( cl.lightlevel );
934 
935 	// send this and the previous cmds in the message, so
936 	// if the last packet was dropped, it can be recovered
937 	oldcmd = NULL;
938 	totalCmds = 0;
939 	totalMsec = 0;
940 	for( i = seq - numDups; i <= seq; i++ ) {
941 		oldest = &cl.history[( i - 1 ) & CMD_MASK];
942 		history = &cl.history[i & CMD_MASK];
943 
944 		numCmds = history->cmdNumber - oldest->cmdNumber;
945 		if( numCmds >= MAX_PACKET_USERCMDS ) {
946 			Com_WPrintf( "CL_SendCmd: MAX_PACKET_USERCMDS exceeded\n" );
947 			SZ_Clear( &msg_write );
948 			break;
949 		}
950 		totalCmds += numCmds;
951 		MSG_WriteBits( numCmds, 5 );
952 		for( j = oldest->cmdNumber + 1; j <= history->cmdNumber; j++ ) {
953 			cmd = &cl.cmds[j & CMD_MASK];
954 			totalMsec += cmd->msec;
955 			bits = MSG_WriteDeltaUsercmd_Enhanced( oldcmd, cmd );
956 			if( cl_showpackets->integer == 3 ) {
957 				MSG_ShowDeltaUsercmdBits_Enhanced( bits );
958 			}
959 			oldcmd = cmd;
960 		}
961 	}
962 
963 	//
964 	// deliver the message
965 	//
966 	i = cls.netchan->Transmit( cls.netchan, msg_write.cursize, msg_write.data );
967 	if( i == -1 ) {
968 		Com_Error( ERR_DISCONNECT, "Connection reset by peer" );
969 	}
970 
971 	SCR_AddLagometerOutPacketInfo( i );
972 
973 	if( cl_showpackets->integer == 1 ) {
974 		Com_Printf( "%i(%i) ", i, totalCmds );
975 	} else if( cl_showpackets->integer == 2 ) {
976 		Com_Printf( "%i(%i) ", i, totalMsec );
977 	} else if( cl_showpackets->integer == 3 ) {
978 		Com_Printf( " | " );
979 	}
980 
981 	SZ_Clear( &msg_write );
982 
983 }
984 
CL_SendUserinfo(void)985 static void CL_SendUserinfo( void ) {
986 	cvar_t *var;
987 	int i;
988 
989 	if( !cls.userinfo_modified ) {
990 		return;
991 	}
992 
993 	if( cls.userinfo_modified == MAX_PACKET_USERINFOS ) {
994 		MSG_WriteByte( clc_userinfo );
995 		MSG_WriteString( Cvar_Userinfo() );
996 		MSG_FlushTo( &cls.netchan->message );
997 	} else if( cls.serverProtocol == PROTOCOL_VERSION_Q2PRO ) {
998 		Com_DPrintf( "Sending %d userinfo updates at frame %u\n",
999 			cls.userinfo_modified, com_framenum );
1000 		for( i = 0; i < cls.userinfo_modified; i++ ) {
1001 			var = cls.userinfo_updates[i];
1002 			MSG_WriteByte( clc_userinfo_delta );
1003 			MSG_WriteString( var->name );
1004 			if( var->flags & CVAR_USERINFO ) {
1005 				MSG_WriteString( var->string );
1006 			} else {
1007 				// no longer in userinfo
1008 				MSG_WriteString( NULL );
1009 			}
1010 		}
1011 		MSG_FlushTo( &cls.netchan->message );
1012 	} else {
1013 		Com_WPrintf( "Userinfo update count is %d, should not happen.\n",
1014 			cls.userinfo_modified );
1015 	}
1016 	cls.userinfo_modified = 0;
1017 
1018 }
1019 
CL_SendCmd(void)1020 void CL_SendCmd( void ) {
1021 	if( cls.state < ca_connected ) {
1022 		return; // not talking to a server
1023 	}
1024 
1025 	if( sv_paused->integer ) {
1026 		return;
1027 	}
1028 
1029 	// generate usercmds while playing a demo,
1030 	// but do not send them
1031 	if( !cls.netchan ) {
1032 		return;
1033 	}
1034 
1035 	if( cls.state < ca_active ) {
1036 		CL_SendUserinfo();
1037 
1038 		// just keepalive or update reliable
1039 		if( cls.netchan->ShouldUpdate( cls.netchan ) ) {
1040 			cls.netchan->Transmit( cls.netchan, 0, NULL );
1041 		}
1042 		cl.lastframe = -1;
1043 		return;
1044 	}
1045 
1046 	if( cl.lastTransmitCmdNumber == cl.cmdNumber ) {
1047 		return; // nothing to send
1048 	}
1049 
1050 	// send a userinfo update if needed
1051 	CL_SendUserinfo();
1052 
1053 	if( cls.serverProtocol == PROTOCOL_VERSION_Q2PRO ) {
1054 		CL_SendBatchedCmd();
1055 	} else {
1056 		CL_SendDefaultCmd();
1057 	}
1058 }
1059 
1060