mirror of
https://bitbucket.org/CPMADevs/cnq3
synced 2024-11-30 15:52:16 +00:00
946 lines
28 KiB
C++
946 lines
28 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// cl.input.c -- builds an intended movement command to send to the server
|
|
|
|
#include "client.h"
|
|
#include "client_help.h"
|
|
|
|
|
|
static char LastKeyTooHigh[K_LAST_KEY < 256 ? 1 : -1];
|
|
|
|
|
|
static cvar_t* m_speed;
|
|
static cvar_t* m_accel;
|
|
static cvar_t* m_accelStyle; // 0=original, 1=new
|
|
static cvar_t* m_accelOffset; // for style 1 only
|
|
static cvar_t* m_limit; // for style 0 only
|
|
static cvar_t* m_pitch;
|
|
static cvar_t* m_yaw;
|
|
static cvar_t* m_forward;
|
|
static cvar_t* m_side;
|
|
static cvar_t* m_filter;
|
|
|
|
static cvar_t* cl_pitchspeed;
|
|
static cvar_t* cl_yawspeed;
|
|
static cvar_t* cl_run;
|
|
static cvar_t* cl_anglespeedkey;
|
|
static cvar_t* cl_freelook;
|
|
|
|
static cvar_t* cl_nodelta;
|
|
static cvar_t* cl_showMouseRate;
|
|
|
|
static unsigned frame_msec;
|
|
static int old_com_frameTime;
|
|
|
|
|
|
/*
|
|
===============================================================================
|
|
|
|
KEY BUTTONS
|
|
|
|
Continuous button event tracking is complicated by the fact that two different
|
|
input sources (say, mouse button 1 and the control key) can both press the
|
|
same button, but the button should only be released when both of the
|
|
pressing key have been released.
|
|
|
|
When a key event issues a button command (+forward, +attack, etc), it appends
|
|
its key number as argv(1) so it can be matched up with the release.
|
|
|
|
argv(2) will be set to the time the event happened, which allows exact
|
|
control even at low framerates when the down and up events may both get qued
|
|
at the same time.
|
|
|
|
===============================================================================
|
|
*/
|
|
|
|
|
|
typedef struct {
|
|
int down[2]; // key nums holding it down
|
|
unsigned downtime; // msec timestamp
|
|
unsigned msec; // msec down this frame if both a down and up happened
|
|
qbool active; // current state
|
|
qbool wasPressed; // set when down, not cleared when up
|
|
} kbutton_t;
|
|
|
|
|
|
static kbutton_t in_left, in_right, in_forward, in_back;
|
|
static kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright;
|
|
static kbutton_t in_strafe, in_speed;
|
|
static kbutton_t in_up, in_down;
|
|
static kbutton_t in_buttons[16];
|
|
|
|
|
|
static qbool in_mlooking;
|
|
|
|
static void IN_MLookDown()
|
|
{
|
|
in_mlooking = qtrue;
|
|
}
|
|
|
|
static void IN_MLookUp()
|
|
{
|
|
in_mlooking = qfalse;
|
|
if ( !cl_freelook->integer ) {
|
|
cl.viewangles[PITCH] = -SHORT2ANGLE(cl.snap.ps.delta_angles[PITCH]);
|
|
}
|
|
}
|
|
|
|
|
|
static void IN_KeyDown( kbutton_t *b )
|
|
{
|
|
const char* c = Cmd_Argv(1);
|
|
int k = (c[0] ? atoi(c) : -1); // -1 == typed manually at the console for continuous down
|
|
|
|
if ( k == b->down[0] || k == b->down[1] ) {
|
|
return; // repeating key
|
|
}
|
|
|
|
if ( !b->down[0] ) {
|
|
b->down[0] = k;
|
|
} else if ( !b->down[1] ) {
|
|
b->down[1] = k;
|
|
} else {
|
|
Com_Printf ("Three keys down for a button!\n");
|
|
return;
|
|
}
|
|
|
|
if ( b->active ) {
|
|
return; // still down
|
|
}
|
|
|
|
// save timestamp for partial frame summing
|
|
c = Cmd_Argv(2);
|
|
b->downtime = atoi(c);
|
|
|
|
b->active = qtrue;
|
|
b->wasPressed = qtrue;
|
|
}
|
|
|
|
static void IN_KeyUp( kbutton_t *b )
|
|
{
|
|
int k;
|
|
const char* c = Cmd_Argv(1);
|
|
|
|
if ( c[0] ) {
|
|
k = atoi(c);
|
|
} else {
|
|
// typed manually at the console, assume for unsticking, so clear all
|
|
b->down[0] = b->down[1] = 0;
|
|
b->active = qfalse;
|
|
return;
|
|
}
|
|
|
|
if ( b->down[0] == k ) {
|
|
b->down[0] = 0;
|
|
} else if ( b->down[1] == k ) {
|
|
b->down[1] = 0;
|
|
} else {
|
|
return; // key up without coresponding down (menu pass through)
|
|
}
|
|
if ( b->down[0] || b->down[1] ) {
|
|
return; // some other key is still holding it down
|
|
}
|
|
|
|
b->active = qfalse;
|
|
|
|
// save timestamp for partial frame summing
|
|
c = Cmd_Argv(2);
|
|
unsigned uptime = atoi(c);
|
|
if ( uptime ) {
|
|
b->msec += uptime - b->downtime;
|
|
} else {
|
|
b->msec += frame_msec / 2;
|
|
}
|
|
|
|
b->active = qfalse;
|
|
}
|
|
|
|
|
|
// returns the fraction of the frame that the key was down
|
|
|
|
static float CL_KeyState( kbutton_t *key )
|
|
{
|
|
int msec = key->msec;
|
|
key->msec = 0;
|
|
|
|
if ( key->active ) {
|
|
// still down
|
|
if ( !key->downtime ) {
|
|
msec = com_frameTime;
|
|
} else {
|
|
msec += com_frameTime - key->downtime;
|
|
}
|
|
key->downtime = com_frameTime;
|
|
}
|
|
|
|
#if 0
|
|
if (msec) {
|
|
Com_Printf ("%i ", msec);
|
|
}
|
|
#endif
|
|
|
|
float val = (float)msec / frame_msec;
|
|
if ( val < 0 ) {
|
|
val = 0;
|
|
}
|
|
if ( val > 1 ) {
|
|
val = 1;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
|
|
static signed char ClampChar( int i )
|
|
{
|
|
if ( i < -128 ) {
|
|
return -128;
|
|
}
|
|
if ( i > 127 ) {
|
|
return 127;
|
|
}
|
|
return i;
|
|
}
|
|
|
|
|
|
// moves the local angle positions
|
|
|
|
static void CL_AdjustAngles()
|
|
{
|
|
float speed;
|
|
|
|
if ( in_speed.active ) {
|
|
speed = ((float)cls.frametime / 1000.0f) * cl_anglespeedkey->value;
|
|
} else {
|
|
speed = (float)cls.frametime / 1000.0f;
|
|
}
|
|
|
|
if ( !in_strafe.active ) {
|
|
cl.viewangles[YAW] -= speed*cl_yawspeed->value*CL_KeyState (&in_right);
|
|
cl.viewangles[YAW] += speed*cl_yawspeed->value*CL_KeyState (&in_left);
|
|
}
|
|
|
|
cl.viewangles[PITCH] -= speed*cl_pitchspeed->value * CL_KeyState (&in_lookup);
|
|
cl.viewangles[PITCH] += speed*cl_pitchspeed->value * CL_KeyState (&in_lookdown);
|
|
}
|
|
|
|
|
|
// sets the usercmd_t based on key states
|
|
|
|
static void CL_KeyMove( usercmd_t *cmd )
|
|
{
|
|
int movespeed;
|
|
int forward, side, up;
|
|
|
|
//
|
|
// adjust for speed key / running
|
|
// the walking flag is to keep animations consistant
|
|
// even during acceleration and develeration
|
|
//
|
|
if ( in_speed.active ^ !!cl_run->integer ) {
|
|
movespeed = 127;
|
|
cmd->buttons &= ~BUTTON_WALKING;
|
|
} else {
|
|
cmd->buttons |= BUTTON_WALKING;
|
|
movespeed = 64;
|
|
}
|
|
|
|
forward = 0;
|
|
side = 0;
|
|
up = 0;
|
|
if ( in_strafe.active ) {
|
|
side += movespeed * CL_KeyState (&in_right);
|
|
side -= movespeed * CL_KeyState (&in_left);
|
|
}
|
|
|
|
side += movespeed * CL_KeyState (&in_moveright);
|
|
side -= movespeed * CL_KeyState (&in_moveleft);
|
|
|
|
up += movespeed * CL_KeyState (&in_up);
|
|
up -= movespeed * CL_KeyState (&in_down);
|
|
|
|
forward += movespeed * CL_KeyState (&in_forward);
|
|
forward -= movespeed * CL_KeyState (&in_back);
|
|
|
|
cmd->forwardmove = ClampChar( forward );
|
|
cmd->rightmove = ClampChar( side );
|
|
cmd->upmove = ClampChar( up );
|
|
}
|
|
|
|
|
|
void CL_MouseEvent( int dx, int dy, int time )
|
|
{
|
|
if ( cls.keyCatchers & KEYCATCH_CONSOLE )
|
|
return;
|
|
|
|
if ( cls.keyCatchers & KEYCATCH_UI ) {
|
|
VM_Call( uivm, UI_MOUSE_EVENT, dx, dy );
|
|
} else if (cls.keyCatchers & KEYCATCH_CGAME) {
|
|
VM_Call( cgvm, CG_MOUSE_EVENT, dx, dy );
|
|
} else {
|
|
if ( cgvm && (cls.cgameForwardInput & 1) )
|
|
VM_Call( cgvm, CG_MOUSE_EVENT, dx, dy );
|
|
cl.mouseDx[cl.mouseIndex] += dx;
|
|
cl.mouseDy[cl.mouseIndex] += dy;
|
|
cl.mouseTime = time;
|
|
}
|
|
}
|
|
|
|
|
|
// joystick values stay set until changed
|
|
|
|
void CL_JoystickEvent( int axis, int value, int time )
|
|
{
|
|
if ( axis < 0 || axis >= MAX_JOYSTICK_AXIS ) {
|
|
Com_Error( ERR_DROP, "CL_JoystickEvent: bad axis %i", axis );
|
|
}
|
|
cl.joystickAxis[axis] = value;
|
|
}
|
|
|
|
|
|
static void CL_JoystickMove( usercmd_t *cmd )
|
|
{
|
|
int movespeed;
|
|
float anglespeed;
|
|
|
|
if ( in_speed.active ^ !!cl_run->integer ) {
|
|
movespeed = 2;
|
|
} else {
|
|
movespeed = 1;
|
|
cmd->buttons |= BUTTON_WALKING;
|
|
}
|
|
|
|
if ( in_speed.active ) {
|
|
anglespeed = ((float)cls.frametime / 1000.0f) * cl_anglespeedkey->value;
|
|
} else {
|
|
anglespeed = (float)cls.frametime / 1000.0f;
|
|
}
|
|
|
|
if ( !in_strafe.active ) {
|
|
cl.viewangles[YAW] += anglespeed * cl_yawspeed->value * cl.joystickAxis[AXIS_SIDE];
|
|
} else {
|
|
cmd->rightmove = ClampChar( cmd->rightmove + cl.joystickAxis[AXIS_SIDE] );
|
|
}
|
|
|
|
if ( in_mlooking ) {
|
|
cl.viewangles[PITCH] += anglespeed * cl_pitchspeed->value * cl.joystickAxis[AXIS_FORWARD];
|
|
} else {
|
|
cmd->forwardmove = ClampChar( cmd->forwardmove + cl.joystickAxis[AXIS_FORWARD] );
|
|
}
|
|
|
|
cmd->upmove = ClampChar( cmd->upmove + cl.joystickAxis[AXIS_UP] );
|
|
}
|
|
|
|
|
|
static void CL_MouseMove( usercmd_t* cmd )
|
|
{
|
|
float mx, my;
|
|
|
|
// allow mouse smoothing
|
|
if ( m_filter->integer ) {
|
|
mx = ( cl.mouseDx[0] + cl.mouseDx[1] ) * 0.5;
|
|
my = ( cl.mouseDy[0] + cl.mouseDy[1] ) * 0.5;
|
|
} else {
|
|
mx = cl.mouseDx[cl.mouseIndex];
|
|
my = cl.mouseDy[cl.mouseIndex];
|
|
}
|
|
cl.mouseIndex ^= 1;
|
|
cl.mouseDx[cl.mouseIndex] = 0;
|
|
cl.mouseDy[cl.mouseIndex] = 0;
|
|
|
|
if (mx == 0.0f && my == 0.0f)
|
|
return;
|
|
|
|
if (m_accel->value != 0.0f) {
|
|
// legacy style
|
|
if (m_accelStyle->integer == 0) {
|
|
const float rate = sqrtf( mx * mx + my * my ) / (float)frame_msec;
|
|
float speed = m_speed->value + rate * m_accel->value;
|
|
|
|
if (m_limit->value != 0.0f && (speed > m_limit->value))
|
|
speed = m_limit->value;
|
|
|
|
if (cl_showMouseRate->integer)
|
|
Com_Printf( "rate: %f, speed: %f\n", rate, speed );
|
|
|
|
mx *= speed;
|
|
my *= speed;
|
|
// new style, similar to quake3e's cl_mouseAccelStyle 1
|
|
} else {
|
|
const float offset = m_accelOffset->value;
|
|
const float rateXa = fabsf( mx ) / (float)frame_msec;
|
|
const float rateYa = fabsf( my ) / (float)frame_msec;
|
|
const float powerXa = powf( rateXa / offset, m_accel->value );
|
|
const float powerYa = powf( rateYa / offset, m_accel->value );
|
|
const float powerX = mx >= 0 ? powerXa : -powerXa;
|
|
const float powerY = my >= 0 ? powerYa : -powerYa;
|
|
|
|
mx = m_speed->value * ( mx + powerX * offset );
|
|
my = m_speed->value * ( my + powerY * offset );
|
|
|
|
if (cl_showMouseRate->integer)
|
|
Com_Printf( "ratex: %f, ratey: %f, powx: %f, powy: %f\n", rateXa, rateYa, powerX, powerY );
|
|
}
|
|
} else {
|
|
float speed = m_speed->value;
|
|
|
|
if (m_limit->value != 0.0f && speed > m_limit->value)
|
|
speed = m_limit->value;
|
|
|
|
mx *= speed;
|
|
my *= speed;
|
|
}
|
|
|
|
// scale by FOV (+zoom only)
|
|
mx *= cl.cgameSensitivity;
|
|
my *= cl.cgameSensitivity;
|
|
|
|
// add mouse X/Y movement to cmd
|
|
if ( in_strafe.active ) {
|
|
cmd->rightmove = ClampChar( cmd->rightmove + m_side->value * mx );
|
|
} else {
|
|
cl.viewangles[YAW] -= m_yaw->value * mx;
|
|
}
|
|
|
|
if ( (in_mlooking || cl_freelook->integer) && !in_strafe.active ) {
|
|
cl.viewangles[PITCH] += m_pitch->value * my;
|
|
} else {
|
|
cmd->forwardmove = ClampChar( cmd->forwardmove - m_forward->value * my );
|
|
}
|
|
}
|
|
|
|
|
|
static void CL_CmdButtons( usercmd_t *cmd )
|
|
{
|
|
int i;
|
|
|
|
// figure button bits
|
|
// send a button bit even if the key was pressed and released in
|
|
// less than a frame
|
|
//
|
|
for (i = 0 ; i < 15 ; i++) {
|
|
if ( in_buttons[i].active || in_buttons[i].wasPressed ) {
|
|
cmd->buttons |= 1 << i;
|
|
}
|
|
in_buttons[i].wasPressed = qfalse;
|
|
}
|
|
|
|
if ( cls.keyCatchers ) {
|
|
cmd->buttons |= BUTTON_TALK;
|
|
}
|
|
|
|
// allow the game to know if any key at all is
|
|
// currently pressed, even if it isn't bound to anything
|
|
if ( anykeydown && !cls.keyCatchers ) {
|
|
cmd->buttons |= BUTTON_ANY;
|
|
}
|
|
}
|
|
|
|
|
|
static void CL_FinishMove( usercmd_t *cmd )
|
|
{
|
|
// copy the state that the cgame is currently sending
|
|
cmd->weapon = cl.cgameUserCmdValue;
|
|
|
|
// send the current server time so the amount of movement
|
|
// can be determined without allowing cheating
|
|
cmd->serverTime = cl.serverTime;
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
|
|
}
|
|
}
|
|
|
|
|
|
static usercmd_t CL_CreateCmd()
|
|
{
|
|
usercmd_t cmd;
|
|
vec3_t oldAngles;
|
|
|
|
VectorCopy( cl.viewangles, oldAngles );
|
|
|
|
// keyboard angle adjustment
|
|
CL_AdjustAngles();
|
|
|
|
Com_Memset( &cmd, 0, sizeof( cmd ) );
|
|
|
|
CL_CmdButtons( &cmd );
|
|
|
|
// get basic movement from keyboard
|
|
CL_KeyMove( &cmd );
|
|
|
|
// get basic movement from mouse
|
|
CL_MouseMove( &cmd );
|
|
cl.userCmdTime = Sys_Milliseconds();
|
|
|
|
// get basic movement from joystick
|
|
CL_JoystickMove( &cmd );
|
|
|
|
// check to make sure the angles haven't wrapped
|
|
if ( cl.viewangles[PITCH] - oldAngles[PITCH] > 90 ) {
|
|
cl.viewangles[PITCH] = oldAngles[PITCH] + 90;
|
|
} else if ( oldAngles[PITCH] - cl.viewangles[PITCH] > 90 ) {
|
|
cl.viewangles[PITCH] = oldAngles[PITCH] - 90;
|
|
}
|
|
|
|
// store out the final values
|
|
CL_FinishMove( &cmd );
|
|
|
|
// draw debug graphs of turning for mouse testing
|
|
if ( cl_debugMove->integer ) {
|
|
if ( cl_debugMove->integer == 1 ) {
|
|
SCR_DebugGraph( fabs(cl.viewangles[YAW] - oldAngles[YAW]), 0 );
|
|
}
|
|
if ( cl_debugMove->integer == 2 ) {
|
|
SCR_DebugGraph( fabs(cl.viewangles[PITCH] - oldAngles[PITCH]), 0 );
|
|
}
|
|
}
|
|
|
|
return cmd;
|
|
}
|
|
|
|
|
|
// create a new usercmd_t structure for this frame
|
|
|
|
static void CL_CreateNewCommands()
|
|
{
|
|
// no need to create usercmds until we have a gamestate
|
|
if ( cls.state < CA_PRIMED ) {
|
|
return;
|
|
}
|
|
|
|
frame_msec = com_frameTime - old_com_frameTime;
|
|
|
|
if (!clc.demoplaying)
|
|
{
|
|
// if running less than 5fps, truncate the extra time to prevent
|
|
// unexpected moves after a hitch
|
|
if ( frame_msec > 200 ) {
|
|
frame_msec = 200;
|
|
} else if (frame_msec < 1) {
|
|
// too fast input
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
old_com_frameTime = com_frameTime;
|
|
|
|
// generate a command for this frame
|
|
cl.cmdNumber++;
|
|
int cmdNum = cl.cmdNumber & CMD_MASK;
|
|
cl.cmds[cmdNum] = CL_CreateCmd();
|
|
}
|
|
|
|
|
|
/*
|
|
Returns false if we are over the maxpackets limit
|
|
and should choke back the bandwidth a bit by not sending
|
|
a packet this frame. All the commands will still get
|
|
delivered in the next packet, but saving a header and
|
|
getting more delta compression will reduce total bandwidth.
|
|
*/
|
|
static qbool CL_ReadyToSendPacket()
|
|
{
|
|
int oldPacketNum;
|
|
int delta;
|
|
|
|
// don't send anything if playing back a demo
|
|
if ( clc.demoplaying || cls.state == CA_CINEMATIC ) {
|
|
return qfalse;
|
|
}
|
|
|
|
// If we are downloading, we send no less than 50ms between packets
|
|
if ( *clc.downloadTempName &&
|
|
cls.realtime - clc.lastPacketSentTime < 50 ) {
|
|
return qfalse;
|
|
}
|
|
|
|
// if we don't have a valid gamestate yet, only send
|
|
// one packet a second
|
|
if ( cls.state != CA_ACTIVE &&
|
|
cls.state != CA_PRIMED &&
|
|
!*clc.downloadTempName &&
|
|
cls.realtime - clc.lastPacketSentTime < 1000 ) {
|
|
return qfalse;
|
|
}
|
|
|
|
// send every frame for loopbacks
|
|
if ( clc.netchan.remoteAddress.type == NA_LOOPBACK ) {
|
|
return qtrue;
|
|
}
|
|
|
|
// send every frame for LAN
|
|
if ( Sys_IsLANAddress( clc.netchan.remoteAddress ) ) {
|
|
return qtrue;
|
|
}
|
|
|
|
oldPacketNum = (clc.netchan.outgoingSequence - 1) & PACKET_MASK;
|
|
delta = cls.realtime - cl.outPackets[ oldPacketNum ].p_realtime;
|
|
if ( delta < 1000 / cl_maxpackets->integer ) {
|
|
// the accumulated commands will go out in the next packet
|
|
return qfalse;
|
|
}
|
|
|
|
return qtrue;
|
|
}
|
|
|
|
/*
|
|
===================
|
|
CL_WritePacket
|
|
|
|
Create and send the command packet to the server
|
|
Including both the reliable commands and the usercmds
|
|
|
|
During normal gameplay, a client packet will contain something like:
|
|
|
|
4 sequence number
|
|
2 qport
|
|
4 serverid
|
|
4 acknowledged sequence number
|
|
4 clc.serverCommandSequence
|
|
<optional reliable commands>
|
|
1 clc_move or clc_moveNoDelta
|
|
1 command count
|
|
<count * usercmds>
|
|
|
|
===================
|
|
*/
|
|
void CL_WritePacket( void )
|
|
{
|
|
|
|
// don't send anything if playing back a demo
|
|
if ( clc.demoplaying || cls.state == CA_CINEMATIC ) {
|
|
return;
|
|
}
|
|
|
|
msg_t buf;
|
|
byte data[MAX_MSGLEN];
|
|
int i, j;
|
|
usercmd_t *cmd, *oldcmd;
|
|
usercmd_t nullcmd;
|
|
int packetNum;
|
|
int oldPacketNum;
|
|
int count, key;
|
|
|
|
Com_Memset( &nullcmd, 0, sizeof(nullcmd) );
|
|
oldcmd = &nullcmd;
|
|
|
|
MSG_Init( &buf, data, sizeof(data) );
|
|
|
|
MSG_Bitstream( &buf );
|
|
// write the current serverId so the server
|
|
// can tell if this is from the current gameState
|
|
MSG_WriteLong( &buf, cl.serverId );
|
|
|
|
// write the last message we received, which can
|
|
// be used for delta compression, and is also used
|
|
// to tell if we dropped a gamestate
|
|
MSG_WriteLong( &buf, clc.serverMessageSequence );
|
|
|
|
// write the last reliable message we received
|
|
MSG_WriteLong( &buf, clc.serverCommandSequence );
|
|
|
|
// write any unacknowledged clientCommands
|
|
for ( i = clc.reliableAcknowledge + 1 ; i <= clc.reliableSequence ; i++ ) {
|
|
MSG_WriteByte( &buf, clc_clientCommand );
|
|
MSG_WriteLong( &buf, i );
|
|
MSG_WriteString( &buf, clc.reliableCommands[ i & (MAX_RELIABLE_COMMANDS-1) ] );
|
|
}
|
|
|
|
// we want to send all the usercmds that were generated in the last
|
|
// few packet, so even if a couple packets are dropped in a row,
|
|
// all the cmds will make it to the server
|
|
oldPacketNum = (clc.netchan.outgoingSequence - 1 - cl_packetdup->integer) & PACKET_MASK;
|
|
count = cl.cmdNumber - cl.outPackets[ oldPacketNum ].p_cmdNumber;
|
|
if ( count > MAX_PACKET_USERCMDS ) {
|
|
count = MAX_PACKET_USERCMDS;
|
|
Com_Printf("MAX_PACKET_USERCMDS\n");
|
|
}
|
|
if ( count >= 1 ) {
|
|
if ( cl_showSend->integer ) {
|
|
Com_Printf( "(%i)", count );
|
|
}
|
|
|
|
// begin a client move command
|
|
if ( cl_nodelta->integer || !cl.snap.valid || clc.demowaiting
|
|
|| clc.serverMessageSequence != cl.snap.messageNum ) {
|
|
MSG_WriteByte (&buf, clc_moveNoDelta);
|
|
} else {
|
|
MSG_WriteByte (&buf, clc_move);
|
|
}
|
|
|
|
// write the command count
|
|
MSG_WriteByte( &buf, count );
|
|
|
|
// use the checksum feed in the key
|
|
key = clc.checksumFeed;
|
|
// also use the message acknowledge
|
|
key ^= clc.serverMessageSequence;
|
|
// also use the last acknowledged server command in the key
|
|
key ^= Com_HashKey(clc.serverCommands[ clc.serverCommandSequence & (MAX_RELIABLE_COMMANDS-1) ], 32);
|
|
|
|
// write all the commands, including the predicted command
|
|
for ( i = 0 ; i < count ; i++ ) {
|
|
j = (cl.cmdNumber - count + i + 1) & CMD_MASK;
|
|
cmd = &cl.cmds[j];
|
|
MSG_WriteDeltaUsercmdKey (&buf, key, oldcmd, cmd);
|
|
oldcmd = cmd;
|
|
}
|
|
}
|
|
|
|
//
|
|
// deliver the message
|
|
//
|
|
packetNum = clc.netchan.outgoingSequence & PACKET_MASK;
|
|
cl.outPackets[ packetNum ].p_realtime = cls.realtime;
|
|
cl.outPackets[ packetNum ].p_serverTime = oldcmd->serverTime;
|
|
cl.outPackets[ packetNum ].p_cmdNumber = cl.cmdNumber;
|
|
clc.lastPacketSentTime = cls.realtime;
|
|
|
|
if ( cl_showSend->integer ) {
|
|
Com_Printf( "%i ", buf.cursize );
|
|
}
|
|
|
|
CL_Netchan_Transmit (&clc.netchan, &buf);
|
|
|
|
// clients never really should have messages large enough
|
|
// to fragment, but in case they do, fire them all off
|
|
// at once
|
|
// TTimo: this causes a packet burst, which is bad karma for winsock
|
|
// added a WARNING message, we'll see if there are legit situations where this happens
|
|
while ( clc.netchan.unsentFragments ) {
|
|
Com_DPrintf( "WARNING: #462 unsent fragments (not supposed to happen!)\n" );
|
|
CL_Netchan_TransmitNextFragment( &clc.netchan );
|
|
}
|
|
}
|
|
|
|
|
|
// called every frame to build and send a command packet to the server
|
|
|
|
void CL_SendCmd()
|
|
{
|
|
// don't send any message if not connected
|
|
if ( cls.state < CA_CONNECTED ) {
|
|
return;
|
|
}
|
|
|
|
// don't send commands if paused
|
|
if ( com_sv_running->integer && sv_paused->integer && cl_paused->integer ) {
|
|
return;
|
|
}
|
|
|
|
// we create commands even if a demo is playing,
|
|
CL_CreateNewCommands();
|
|
|
|
// don't send a packet if the last packet was sent too recently
|
|
if ( !CL_ReadyToSendPacket() ) {
|
|
if ( cl_showSend->integer ) {
|
|
Com_Printf( ". " );
|
|
}
|
|
return;
|
|
}
|
|
|
|
CL_WritePacket();
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
|
|
static void IN_UpDown(void) {IN_KeyDown(&in_up);}
|
|
static void IN_UpUp(void) {IN_KeyUp(&in_up);}
|
|
static void IN_DownDown(void) {IN_KeyDown(&in_down);}
|
|
static void IN_DownUp(void) {IN_KeyUp(&in_down);}
|
|
static void IN_LeftDown(void) {IN_KeyDown(&in_left);}
|
|
static void IN_LeftUp(void) {IN_KeyUp(&in_left);}
|
|
static void IN_RightDown(void) {IN_KeyDown(&in_right);}
|
|
static void IN_RightUp(void) {IN_KeyUp(&in_right);}
|
|
static void IN_ForwardDown(void) {IN_KeyDown(&in_forward);}
|
|
static void IN_ForwardUp(void) {IN_KeyUp(&in_forward);}
|
|
static void IN_BackDown(void) {IN_KeyDown(&in_back);}
|
|
static void IN_BackUp(void) {IN_KeyUp(&in_back);}
|
|
static void IN_LookupDown(void) {IN_KeyDown(&in_lookup);}
|
|
static void IN_LookupUp(void) {IN_KeyUp(&in_lookup);}
|
|
static void IN_LookdownDown(void) {IN_KeyDown(&in_lookdown);}
|
|
static void IN_LookdownUp(void) {IN_KeyUp(&in_lookdown);}
|
|
static void IN_MoveleftDown(void) {IN_KeyDown(&in_moveleft);}
|
|
static void IN_MoveleftUp(void) {IN_KeyUp(&in_moveleft);}
|
|
static void IN_MoverightDown(void) {IN_KeyDown(&in_moveright);}
|
|
static void IN_MoverightUp(void) {IN_KeyUp(&in_moveright);}
|
|
|
|
static void IN_SpeedDown(void) {IN_KeyDown(&in_speed);}
|
|
static void IN_SpeedUp(void) {IN_KeyUp(&in_speed);}
|
|
static void IN_StrafeDown(void) {IN_KeyDown(&in_strafe);}
|
|
static void IN_StrafeUp(void) {IN_KeyUp(&in_strafe);}
|
|
|
|
static void IN_Button0Down(void) {IN_KeyDown(&in_buttons[0]);}
|
|
static void IN_Button0Up(void) {IN_KeyUp(&in_buttons[0]);}
|
|
static void IN_Button1Down(void) {IN_KeyDown(&in_buttons[1]);}
|
|
static void IN_Button1Up(void) {IN_KeyUp(&in_buttons[1]);}
|
|
static void IN_Button2Down(void) {IN_KeyDown(&in_buttons[2]);}
|
|
static void IN_Button2Up(void) {IN_KeyUp(&in_buttons[2]);}
|
|
static void IN_Button3Down(void) {IN_KeyDown(&in_buttons[3]);}
|
|
static void IN_Button3Up(void) {IN_KeyUp(&in_buttons[3]);}
|
|
static void IN_Button4Down(void) {IN_KeyDown(&in_buttons[4]);}
|
|
static void IN_Button4Up(void) {IN_KeyUp(&in_buttons[4]);}
|
|
static void IN_Button5Down(void) {IN_KeyDown(&in_buttons[5]);}
|
|
static void IN_Button5Up(void) {IN_KeyUp(&in_buttons[5]);}
|
|
static void IN_Button6Down(void) {IN_KeyDown(&in_buttons[6]);}
|
|
static void IN_Button6Up(void) {IN_KeyUp(&in_buttons[6]);}
|
|
static void IN_Button7Down(void) {IN_KeyDown(&in_buttons[7]);}
|
|
static void IN_Button7Up(void) {IN_KeyUp(&in_buttons[7]);}
|
|
static void IN_Button8Down(void) {IN_KeyDown(&in_buttons[8]);}
|
|
static void IN_Button8Up(void) {IN_KeyUp(&in_buttons[8]);}
|
|
static void IN_Button9Down(void) {IN_KeyDown(&in_buttons[9]);}
|
|
static void IN_Button9Up(void) {IN_KeyUp(&in_buttons[9]);}
|
|
static void IN_Button10Down(void) {IN_KeyDown(&in_buttons[10]);}
|
|
static void IN_Button10Up(void) {IN_KeyUp(&in_buttons[10]);}
|
|
static void IN_Button11Down(void) {IN_KeyDown(&in_buttons[11]);}
|
|
static void IN_Button11Up(void) {IN_KeyUp(&in_buttons[11]);}
|
|
static void IN_Button12Down(void) {IN_KeyDown(&in_buttons[12]);}
|
|
static void IN_Button12Up(void) {IN_KeyUp(&in_buttons[12]);}
|
|
static void IN_Button13Down(void) {IN_KeyDown(&in_buttons[13]);}
|
|
static void IN_Button13Up(void) {IN_KeyUp(&in_buttons[13]);}
|
|
static void IN_Button14Down(void) {IN_KeyDown(&in_buttons[14]);}
|
|
static void IN_Button14Up(void) {IN_KeyUp(&in_buttons[14]);}
|
|
static void IN_Button15Down(void) {IN_KeyDown(&in_buttons[15]);}
|
|
static void IN_Button15Up(void) {IN_KeyUp(&in_buttons[15]);}
|
|
|
|
|
|
static void IN_Restart_f()
|
|
{
|
|
Sys_ShutdownInput();
|
|
CL_ShutdownInput();
|
|
|
|
CL_InitInput();
|
|
Sys_InitInput();
|
|
}
|
|
|
|
|
|
static const cvarTableItem_t cl_cvars[] =
|
|
{
|
|
{ NULL, "cl_drawMouseLag", "0", 0, CVART_BOOL, NULL, NULL, "draws sampling to display/upload delays" },
|
|
{ &m_speed, "m_speed", "2", CVAR_ARCHIVE, CVART_FLOAT, "0", "100", "mouse sensitivity" },
|
|
{ &m_accel, "m_accel", "0", CVAR_ARCHIVE, CVART_FLOAT, "0", NULL, "mouse acceleration" },
|
|
{ &m_accelStyle, "m_accelStyle", "0", CVAR_ARCHIVE, CVART_INTEGER, "0", "1", help_m_accelStyle },
|
|
{ &m_accelOffset, "m_accelOffset", "5", CVAR_ARCHIVE, CVART_FLOAT, "0.001", "5000", help_m_accelOffset },
|
|
{ &m_limit, "m_limit", "0", CVAR_ARCHIVE, CVART_FLOAT, "0", NULL, help_m_limit },
|
|
{ &m_pitch, "m_pitch", "0.022", CVAR_ARCHIVE, CVART_FLOAT, "-100", "100", "post-accel vertical mouse sens." },
|
|
{ &m_yaw, "m_yaw", "0.022", CVAR_ARCHIVE, CVART_FLOAT, "-100", "100", "post-accel horizontal mouse sens." },
|
|
{ &m_forward, "m_forward", "0.25", CVAR_ARCHIVE, CVART_FLOAT, "-32767", "32767", help_m_forward },
|
|
{ &m_side, "m_side", "0.25", CVAR_ARCHIVE, CVART_FLOAT, "-32767", "32767", help_m_side },
|
|
{ &m_filter, "m_filter", "0", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, "mouse smoothing" },
|
|
{ &cl_pitchspeed, "cl_pitchspeed", "140", CVAR_ARCHIVE, CVART_FLOAT, "0", NULL, help_cl_pitchspeed },
|
|
{ &cl_yawspeed, "cl_yawspeed", "140", CVAR_ARCHIVE, CVART_FLOAT, "0", NULL, help_cl_yawspeed },
|
|
{ &cl_anglespeedkey, "cl_anglespeedkey", "1.5", 0, CVART_FLOAT },
|
|
{ &cl_run, "cl_run", "1", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, help_cl_run },
|
|
{ &cl_freelook, "cl_freelook", "1", CVAR_ARCHIVE, CVART_BOOL, NULL, NULL, help_cl_freelook },
|
|
{ &cl_showMouseRate, "cl_showmouserate", "0", 0, CVART_BOOL, NULL, NULL, help_cl_showMouseRate },
|
|
{ &cl_nodelta, "cl_nodelta", "0", 0, CVART_BOOL, NULL, NULL, "disables delta-compression on uploaded user commands" },
|
|
{ &cl_debugMove, "cl_debugMove", "0", 0, CVART_INTEGER, "0", "2", help_cl_debugMove }
|
|
};
|
|
|
|
|
|
static const cmdTableItem_t cl_cmds[] =
|
|
{
|
|
#define help_button1 "\nYou can't move and there's a chat bubble over your head."
|
|
{ "in_restart", IN_Restart_f, NULL, "restarts the input system" },
|
|
{ "+moveup", IN_UpDown, NULL, "starts jumping/moving up" help_plus_minus },
|
|
{ "-moveup", IN_UpUp, NULL, "stops jumping/moving up" help_plus_minus },
|
|
{ "+movedown", IN_DownDown, NULL, "starts crouching/moving down" help_plus_minus },
|
|
{ "-movedown", IN_DownUp, NULL, "stops crouching/moving down" help_plus_minus },
|
|
{ "+left", IN_LeftDown, NULL, "starts rotating left" help_plus_minus },
|
|
{ "-left", IN_LeftUp, NULL, "stops rotating left" help_plus_minus },
|
|
{ "+right", IN_RightDown, NULL, "starts rotating right" help_plus_minus },
|
|
{ "-right", IN_RightUp, NULL, "stops rotating right" help_plus_minus },
|
|
{ "+forward", IN_ForwardDown, NULL, "starts moving forward" help_plus_minus },
|
|
{ "-forward", IN_ForwardUp, NULL, "stops moving forward" help_plus_minus },
|
|
{ "+back", IN_BackDown, NULL, "starts moving backwards" help_plus_minus },
|
|
{ "-back", IN_BackUp, NULL, "stops moving backwards" help_plus_minus },
|
|
{ "+lookup", IN_LookupDown, NULL, "starts looking up" help_plus_minus },
|
|
{ "-lookup", IN_LookupUp, NULL, "stops looking up" help_plus_minus },
|
|
{ "+lookdown", IN_LookdownDown, NULL, "starts looking down" help_plus_minus },
|
|
{ "-lookdown", IN_LookdownUp, NULL, "stops looking down" help_plus_minus },
|
|
{ "+strafe", IN_StrafeDown, NULL, "starts mouse strafing mode" help_plus_minus },
|
|
{ "-strafe", IN_StrafeUp, NULL, "stops mouse strafing mode" help_plus_minus },
|
|
{ "+moveleft", IN_MoveleftDown, NULL, "starts strafing left" help_plus_minus },
|
|
{ "-moveleft", IN_MoveleftUp, NULL, "stops strafing left" help_plus_minus },
|
|
{ "+moveright", IN_MoverightDown, NULL, "starts strafing right" help_plus_minus },
|
|
{ "-moveright", IN_MoverightUp, NULL, "stops strafing right" help_plus_minus },
|
|
{ "+speed", IN_SpeedDown, NULL, "start walk/run toggle" },
|
|
{ "-speed", IN_SpeedUp, NULL, "stops walk/run toggle" },
|
|
{ "+attack", IN_Button0Down, NULL, "starts firing the gun" help_plus_minus },
|
|
{ "-attack", IN_Button0Up, NULL, "stops firing the gun" help_plus_minus },
|
|
{ "+button0", IN_Button0Down, NULL, "starts firing the gun" help_plus_minus },
|
|
{ "-button0", IN_Button0Up, NULL, "stops firing the gun" help_plus_minus },
|
|
{ "+button1", IN_Button1Down, NULL, "starts chat stance" help_button1 help_plus_minus },
|
|
{ "-button1", IN_Button1Up, NULL, "stops chat stance" help_button1 help_plus_minus },
|
|
{ "+button2", IN_Button2Down, NULL, "starts using item" help_plus_minus },
|
|
{ "-button2", IN_Button2Up, NULL, "stops using item" help_plus_minus },
|
|
{ "+button3", IN_Button3Down, NULL, "starts taunt" help_plus_minus },
|
|
{ "-button3", IN_Button3Up, NULL, "stops taunt" help_plus_minus },
|
|
{ "+button4", IN_Button4Down },
|
|
{ "-button4", IN_Button4Up },
|
|
{ "+button5", IN_Button5Down },
|
|
{ "-button5", IN_Button5Up },
|
|
{ "+button6", IN_Button6Down },
|
|
{ "-button6", IN_Button6Up },
|
|
{ "+button7", IN_Button7Down },
|
|
{ "-button7", IN_Button7Up },
|
|
{ "+button8", IN_Button8Down },
|
|
{ "-button8", IN_Button8Up },
|
|
{ "+button9", IN_Button9Down },
|
|
{ "-button9", IN_Button9Up },
|
|
{ "+button10", IN_Button10Down },
|
|
{ "-button10", IN_Button10Up },
|
|
{ "+button11", IN_Button11Down },
|
|
{ "-button11", IN_Button11Up },
|
|
{ "+button12", IN_Button12Down },
|
|
{ "-button12", IN_Button12Up },
|
|
{ "+button13", IN_Button13Down },
|
|
{ "-button13", IN_Button13Up },
|
|
{ "+button14", IN_Button14Down },
|
|
{ "-button14", IN_Button14Up },
|
|
{ "+button15", IN_Button15Down },
|
|
{ "-button15", IN_Button15Up },
|
|
{ "+mlook", IN_MLookDown, NULL, "enables free look when cl_freelook is 0" help_plus_minus },
|
|
{ "-mlook", IN_MLookUp, NULL, "disables free look when cl_freelook is 0" help_plus_minus }
|
|
#undef help_button1
|
|
};
|
|
|
|
|
|
void CL_InitInput()
|
|
{
|
|
Cmd_RegisterArray( cl_cmds, MODULE_INPUT );
|
|
Cvar_RegisterArray( cl_cvars, MODULE_INPUT );
|
|
}
|
|
|
|
|
|
void CL_ShutdownInput()
|
|
{
|
|
Cmd_UnregisterModule( MODULE_INPUT );
|
|
}
|