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