From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- src/client/cl_input.cpp | 1194 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1194 insertions(+) create mode 100644 src/client/cl_input.cpp (limited to 'src/client/cl_input.cpp') diff --git a/src/client/cl_input.cpp b/src/client/cl_input.cpp new file mode 100644 index 0000000..e134646 --- /dev/null +++ b/src/client/cl_input.cpp @@ -0,0 +1,1194 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2013 Darklegion Development +Copyright (C) 2015-2019 GrangerHub + +This file is part of Tremulous. + +Tremulous 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 3 of the License, +or (at your option) any later version. + +Tremulous 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 Tremulous; if not, see + +=========================================================================== +*/ + +// cl.input.c -- builds an intended movement command to send to the server + +#include "client.h" + +unsigned frame_msec; +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. + +=============================================================================== +*/ + +kbutton_t in_left, in_right, in_forward, in_back; +kbutton_t in_lookup, in_lookdown, in_moveleft, in_moveright; +kbutton_t in_strafe, in_speed; +kbutton_t in_up, in_down; + +#ifdef USE_VOIP +kbutton_t in_voiprecord; +#endif + +kbutton_t in_buttons[16]; + +bool in_mlooking; + +static void IN_CenterView(void) +{ + cl.viewangles[PITCH] = -SHORT2ANGLE( (clc.netchan.alternateProtocol == 2 + ? cl.snap.alternatePs.delta_angles + : cl.snap.ps.delta_angles)[PITCH] ); +} + +static void IN_MLookDown(void) +{ + in_mlooking = true; +} + +static void IN_MLookUp(void) +{ + in_mlooking = false; + if (!cl_freelook->integer) + { + IN_CenterView(); + } +} + +static void IN_KeyDown(kbutton_t *b) +{ + int k; + + const char *c = Cmd_Argv(1); + if (c[0]) + { + k = atoi(c); + } + else + { + k = -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 = true; + b->wasPressed = true; +} + +static void IN_KeyUp(kbutton_t *b) +{ + int k; + unsigned uptime; + + 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 = false; + 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 = false; + + // save timestamp for partial frame summing + c = Cmd_Argv(2); + uptime = atoi(c); + if (uptime) + { + b->msec += uptime - b->downtime; + } + else + { + b->msec += frame_msec / 2; + } + + b->active = false; +} + +/* +=============== +CL_KeyState + +Returns the fraction of the frame that the key was down +=============== +*/ +static float CL_KeyState(kbutton_t *key) +{ + float val; + int msec; + + 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 + + val = (float)msec / frame_msec; + if (val < 0) + { + val = 0; + } + if (val > 1) + { + val = 1; + } + + return val; +} + +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); } + +#ifdef USE_VOIP +static void IN_VoipRecordDown(void) +{ + IN_KeyDown(&in_voiprecord); + Cvar_Set("cl_voipSend", "1"); +} + +static void IN_VoipRecordUp(void) +{ + IN_KeyUp(&in_voiprecord); + Cvar_Set("cl_voipSend", "0"); +} +#endif + +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]); } + +//========================================================================== + +cvar_t *cl_yawspeed; +cvar_t *cl_pitchspeed; + +cvar_t *cl_run; + +cvar_t *cl_anglespeedkey; + +/* +================ +CL_AdjustAngles + +Moves the local angle positions +================ +*/ +static void CL_AdjustAngles(void) +{ + float speed; + + if (in_speed.active) + { + speed = 0.001 * cls.frametime * cl_anglespeedkey->value; + } + else + { + speed = 0.001 * cls.frametime; + } + + 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); +} + +/* +================ +CL_KeyMove + +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); +} + +/* +================= +CL_MouseEvent +================= +*/ +void CL_MouseEvent(int dx, int dy, int time) +{ + if (Key_GetCatcher() & KEYCATCH_UI) + { + VM_Call(cls.ui, UI_MOUSE_EVENT, dx, dy); + } + else if (Key_GetCatcher() & KEYCATCH_CGAME) + { + VM_Call(cls.cgame, CG_MOUSE_EVENT, dx, dy); + } + else + { + cl.mouseDx[cl.mouseIndex] += dx; + cl.mouseDy[cl.mouseIndex] += dy; + } +} + +/* +================= +CL_JoystickEvent + +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; +} + +/* +================= +CL_JoystickMove +================= +*/ +static void CL_JoystickMove(usercmd_t *cmd) +{ + float anglespeed; + + float yaw = j_yaw->value * cl.joystickAxis[j_yaw_axis->integer]; + float right = j_side->value * cl.joystickAxis[j_side_axis->integer]; + float forward = j_forward->value * cl.joystickAxis[j_forward_axis->integer]; + float pitch = j_pitch->value * cl.joystickAxis[j_pitch_axis->integer]; + float up = j_up->value * cl.joystickAxis[j_up_axis->integer]; + + if (!(in_speed.active ^ cl_run->integer)) + { + cmd->buttons |= BUTTON_WALKING; + } + + if (in_speed.active) + { + anglespeed = 0.001 * cls.frametime * cl_anglespeedkey->value; + } + else + { + anglespeed = 0.001 * cls.frametime; + } + + if (!in_strafe.active) + { + cl.viewangles[YAW] += anglespeed * yaw; + cmd->rightmove = ClampChar(cmd->rightmove + (int)right); + } + else + { + cl.viewangles[YAW] += anglespeed * right; + cmd->rightmove = ClampChar(cmd->rightmove + (int)yaw); + } + + if (in_mlooking) + { + cl.viewangles[PITCH] += anglespeed * forward; + cmd->forwardmove = ClampChar(cmd->forwardmove + (int)pitch); + } + else + { + cl.viewangles[PITCH] += anglespeed * pitch; + cmd->forwardmove = ClampChar(cmd->forwardmove + (int)forward); + } + + cmd->upmove = ClampChar(cmd->upmove + (int)up); +} + +/* +================= +CL_MouseMove +================= +*/ + +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.5f; + my = (cl.mouseDy[0] + cl.mouseDy[1]) * 0.5f; + } + 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 (cl_mouseAccel->value != 0.0f) + { + if (cl_mouseAccelStyle->integer == 0) + { + float accelSensitivity; + float rate; + + rate = sqrt(mx * mx + my * my) / (float)frame_msec; + + accelSensitivity = cl_sensitivity->value + rate * cl_mouseAccel->value; + mx *= accelSensitivity; + my *= accelSensitivity; + + if (cl_showMouseRate->integer) Com_Printf("rate: %f, accelSensitivity: %f\n", rate, accelSensitivity); + } + else + { + float rate[2]; + float power[2]; + + // sensitivity remains pretty much unchanged at low speeds + // cl_mouseAccel is a power value to how the acceleration is shaped + // cl_mouseAccelOffset is the rate for which the acceleration will have doubled the non accelerated + // amplification + // NOTE: decouple the config cvars for independent acceleration setup along X and Y? + + rate[0] = fabs(mx) / (float)frame_msec; + rate[1] = fabs(my) / (float)frame_msec; + power[0] = powf(rate[0] / cl_mouseAccelOffset->value, cl_mouseAccel->value); + power[1] = powf(rate[1] / cl_mouseAccelOffset->value, cl_mouseAccel->value); + + mx = cl_sensitivity->value * (mx + ((mx < 0) ? -power[0] : power[0]) * cl_mouseAccelOffset->value); + my = cl_sensitivity->value * (my + ((my < 0) ? -power[1] : power[1]) * cl_mouseAccelOffset->value); + + if (cl_showMouseRate->integer) + Com_Printf("ratex: %f, ratey: %f, powx: %f, powy: %f\n", rate[0], rate[1], power[0], power[1]); + } + } + else + { + mx *= cl_sensitivity->value; + my *= cl_sensitivity->value; + } + + // ingame FOV + 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); +} + +/* +============== +CL_CmdButtons +============== +*/ +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 = false; + } + + if (Key_GetCatcher()) + { + 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 && Key_GetCatcher() == 0) + { + cmd->buttons |= BUTTON_ANY; + } +} + +/* +============== +CL_FinishMove +============== +*/ +static void CL_FinishMove(usercmd_t *cmd) +{ + int i; + + // 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 (i = 0; i < 3; i++) + { + cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]); + } +} + +/* +================= +CL_CreateCmd +================= +*/ +static usercmd_t CL_CreateCmd(void) +{ + usercmd_t cmd; + vec3_t oldAngles; + + VectorCopy(cl.viewangles, oldAngles); + + // keyboard angle adjustment + CL_AdjustAngles(); + + ::memset(&cmd, 0, sizeof(cmd)); + + CL_CmdButtons(&cmd); + + // get basic movement from keyboard + CL_KeyMove(&cmd); + + // get basic movement from mouse + CL_MouseMove(&cmd); + + // 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])); + } + if (cl_debugMove->integer == 2) + { + SCR_DebugGraph(fabs(cl.viewangles[PITCH] - oldAngles[PITCH])); + } + } + + return cmd; +} + +/* +================= +CL_CreateNewCommands + +Create a new usercmd_t structure for this frame +================= +*/ +static void CL_CreateNewCommands(void) +{ + int cmdNum; + + // no need to create usercmds until we have a gamestate + if (clc.state < CA_PRIMED) + { + return; + } + + frame_msec = com_frameTime - old_com_frameTime; + + // if running over 1000fps, act as if each frame is 1ms + // prevents divisions by zero + if (frame_msec < 1) + { + frame_msec = 1; + } + + // if running less than 5fps, truncate the extra time to prevent + // unexpected moves after a hitch + if (frame_msec > 200) + { + frame_msec = 200; + } + old_com_frameTime = com_frameTime; + + // generate a command for this frame + cl.cmdNumber++; + cmdNum = cl.cmdNumber & CMD_MASK; + cl.cmds[cmdNum] = CL_CreateCmd(); +} + +/* +================= +CL_ReadyToSendPacket + +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 bool CL_ReadyToSendPacket(void) +{ + int oldPacketNum; + int delta; + + // don't send anything if playing back a demo + if (clc.demoplaying || clc.state == CA_CINEMATIC) + { + return false; + } + + // If we are downloading, we send no less than 50ms between packets + if (*clc.downloadTempName && cls.realtime - clc.lastPacketSentTime < 50) + { + return false; + } + + // if we don't have a valid gamestate yet, only send + // one packet a second + if (clc.state != CA_ACTIVE && clc.state != CA_PRIMED && !*clc.downloadTempName && + cls.realtime - clc.lastPacketSentTime < 1000) + { + return false; + } + + // send every frame for loopbacks + if (clc.netchan.remoteAddress.type == NA_LOOPBACK) + { + return true; + } + + // send every frame for LAN + if (cl_lanForcePackets->integer && Sys_IsLANAddress(clc.netchan.remoteAddress)) + { + return true; + } + + // check for exceeding cl_maxpackets + if (cl_maxpackets->integer < 15) + { + Cvar_Set("cl_maxpackets", "15"); + } + else if (cl_maxpackets->integer > 125) + { + Cvar_Set("cl_maxpackets", "125"); + } + 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 false; + } + + return true; +} + +/* +=================== +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 + +1 clc_move or clc_moveNoDelta +1 command count + + +=================== +*/ +void CL_WritePacket(void) +{ + msg_t buf; + byte data[MAX_MSGLEN]; + int i, j; + usercmd_t *cmd, *oldcmd; + usercmd_t nullcmd; + int packetNum; + int oldPacketNum; + int count, key; + + // don't send anything if playing back a demo + if (clc.demoplaying || clc.state == CA_CINEMATIC) + { + return; + } + + ::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 + if (cl_packetdup->integer < 0) + { + Cvar_Set("cl_packetdup", "0"); + } + else if (cl_packetdup->integer > 5) + { + Cvar_Set("cl_packetdup", "5"); + } + 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"); + } + +#ifdef USE_VOIP + if (clc.voipOutgoingDataSize > 0) + { + if ((clc.voipFlags & VOIP_SPATIAL) || Com_IsVoipTarget(clc.voipTargets, sizeof(clc.voipTargets), -1)) + { + if (clc.netchan.alternateProtocol != 0) + { + MSG_WriteByte(&buf, clc_EOF); + } + MSG_WriteByte(&buf, clc_voipSpeex); + if (clc.netchan.alternateProtocol != 0) + { + MSG_WriteByte(&buf, clc_voipSpeex + 1); + } + MSG_WriteByte(&buf, clc.voipOutgoingGeneration); + MSG_WriteLong(&buf, clc.voipOutgoingSequence); + MSG_WriteByte(&buf, clc.voipOutgoingDataFrames); + if (clc.netchan.alternateProtocol == 0) + { + MSG_WriteData(&buf, clc.voipTargets, sizeof(clc.voipTargets)); + MSG_WriteByte(&buf, clc.voipFlags); + } + else + { + MSG_WriteLong(&buf, clc.voipTargets[0] | (clc.voipTargets[1] << 8) | (clc.voipTargets[2] << 16) | + ((clc.voipTargets[3] & 0x7F) << 24)); + MSG_WriteLong(&buf, (clc.voipTargets[3] >> 7) | (clc.voipTargets[4] << 1) | (clc.voipTargets[5] << 9) | + (clc.voipTargets[6] << 17) | ((clc.voipTargets[7] & 0x3F) << 25)); + MSG_WriteLong(&buf, clc.voipTargets[7] >> 6); + } + MSG_WriteShort(&buf, clc.voipOutgoingDataSize); + MSG_WriteData(&buf, clc.voipOutgoingData, clc.voipOutgoingDataSize); + + // If we're recording a demo, we have to fake a server packet with + // this VoIP data so it gets to disk; the server doesn't send it + // back to us, and we might as well eliminate concerns about dropped + // and misordered packets here. + if (clc.demorecording && !clc.demowaiting) + { + const int voipSize = clc.voipOutgoingDataSize; + msg_t fakemsg; + byte fakedata[MAX_MSGLEN]; + MSG_Init(&fakemsg, fakedata, sizeof(fakedata)); + MSG_Bitstream(&fakemsg); + MSG_WriteLong(&fakemsg, clc.reliableAcknowledge); + MSG_WriteByte(&fakemsg, svc_voipOpus); + MSG_WriteShort(&fakemsg, clc.clientNum); + MSG_WriteByte(&fakemsg, clc.voipOutgoingGeneration); + MSG_WriteLong(&fakemsg, clc.voipOutgoingSequence); + MSG_WriteByte(&fakemsg, clc.voipOutgoingDataFrames); + MSG_WriteShort(&fakemsg, clc.voipOutgoingDataSize); + if (clc.netchan.alternateProtocol == 0) + { + MSG_WriteBits(&fakemsg, clc.voipFlags, VOIP_FLAGCNT); + } + MSG_WriteData(&fakemsg, clc.voipOutgoingData, voipSize); + MSG_WriteByte(&fakemsg, svc_EOF); + CL_WriteDemoMessage(&fakemsg, 0); + } + + clc.voipOutgoingSequence += clc.voipOutgoingDataFrames; + clc.voipOutgoingDataSize = 0; + clc.voipOutgoingDataFrames = 0; + } + else + { + // We have data, but no targets. Silently discard all data + clc.voipOutgoingDataSize = 0; + clc.voipOutgoingDataFrames = 0; + } + } +#endif + + 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 ^= MSG_HashKey(clc.netchan.alternateProtocol, + 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); +} + +/* +================= +CL_SendCmd + +Called every frame to builds and sends a command packet to the server. +================= +*/ +void CL_SendCmd(void) +{ + // don't send any message if not connected + if (clc.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(); +} + +/* +============ +CL_InitInput +============ +*/ +void CL_InitInput(void) +{ + Cmd_AddCommand("centerview", IN_CenterView); + + Cmd_AddCommand("+moveup", IN_UpDown); + Cmd_AddCommand("-moveup", IN_UpUp); + Cmd_AddCommand("+movedown", IN_DownDown); + Cmd_AddCommand("-movedown", IN_DownUp); + Cmd_AddCommand("+left", IN_LeftDown); + Cmd_AddCommand("-left", IN_LeftUp); + Cmd_AddCommand("+right", IN_RightDown); + Cmd_AddCommand("-right", IN_RightUp); + Cmd_AddCommand("+forward", IN_ForwardDown); + Cmd_AddCommand("-forward", IN_ForwardUp); + Cmd_AddCommand("+back", IN_BackDown); + Cmd_AddCommand("-back", IN_BackUp); + Cmd_AddCommand("+lookup", IN_LookupDown); + Cmd_AddCommand("-lookup", IN_LookupUp); + Cmd_AddCommand("+lookdown", IN_LookdownDown); + Cmd_AddCommand("-lookdown", IN_LookdownUp); + Cmd_AddCommand("+strafe", IN_StrafeDown); + Cmd_AddCommand("-strafe", IN_StrafeUp); + Cmd_AddCommand("+moveleft", IN_MoveleftDown); + Cmd_AddCommand("-moveleft", IN_MoveleftUp); + Cmd_AddCommand("+moveright", IN_MoverightDown); + Cmd_AddCommand("-moveright", IN_MoverightUp); + Cmd_AddCommand("+speed", IN_SpeedDown); + Cmd_AddCommand("-speed", IN_SpeedUp); + Cmd_AddCommand("+attack", IN_Button0Down); + Cmd_AddCommand("-attack", IN_Button0Up); + Cmd_AddCommand("+button0", IN_Button0Down); + Cmd_AddCommand("-button0", IN_Button0Up); + Cmd_AddCommand("+button1", IN_Button1Down); + Cmd_AddCommand("-button1", IN_Button1Up); + Cmd_AddCommand("+button2", IN_Button2Down); + Cmd_AddCommand("-button2", IN_Button2Up); + Cmd_AddCommand("+button3", IN_Button3Down); + Cmd_AddCommand("-button3", IN_Button3Up); + Cmd_AddCommand("+button4", IN_Button4Down); + Cmd_AddCommand("-button4", IN_Button4Up); + Cmd_AddCommand("+button5", IN_Button5Down); + Cmd_AddCommand("-button5", IN_Button5Up); + Cmd_AddCommand("+button6", IN_Button6Down); + Cmd_AddCommand("-button6", IN_Button6Up); + Cmd_AddCommand("+button7", IN_Button7Down); + Cmd_AddCommand("-button7", IN_Button7Up); + Cmd_AddCommand("+button8", IN_Button8Down); + Cmd_AddCommand("-button8", IN_Button8Up); + Cmd_AddCommand("+button9", IN_Button9Down); + Cmd_AddCommand("-button9", IN_Button9Up); + Cmd_AddCommand("+button10", IN_Button10Down); + Cmd_AddCommand("-button10", IN_Button10Up); + Cmd_AddCommand("+button11", IN_Button11Down); + Cmd_AddCommand("-button11", IN_Button11Up); + Cmd_AddCommand("+button12", IN_Button12Down); + Cmd_AddCommand("-button12", IN_Button12Up); + Cmd_AddCommand("+button13", IN_Button13Down); + Cmd_AddCommand("-button13", IN_Button13Up); + Cmd_AddCommand("+button14", IN_Button14Down); + Cmd_AddCommand("-button14", IN_Button14Up); + Cmd_AddCommand("+mlook", IN_MLookDown); + Cmd_AddCommand("-mlook", IN_MLookUp); + +#ifdef USE_VOIP + Cmd_AddCommand("+voiprecord", IN_VoipRecordDown); + Cmd_AddCommand("-voiprecord", IN_VoipRecordUp); +#endif + + cl_nodelta = Cvar_Get("cl_nodelta", "0", 0); + cl_debugMove = Cvar_Get("cl_debugMove", "0", 0); +} + +/* +============ +CL_ShutdownInput +============ +*/ +void CL_ShutdownInput(void) +{ + Cmd_RemoveCommand("centerview"); + + Cmd_RemoveCommand("+moveup"); + Cmd_RemoveCommand("-moveup"); + Cmd_RemoveCommand("+movedown"); + Cmd_RemoveCommand("-movedown"); + Cmd_RemoveCommand("+left"); + Cmd_RemoveCommand("-left"); + Cmd_RemoveCommand("+right"); + Cmd_RemoveCommand("-right"); + Cmd_RemoveCommand("+forward"); + Cmd_RemoveCommand("-forward"); + Cmd_RemoveCommand("+back"); + Cmd_RemoveCommand("-back"); + Cmd_RemoveCommand("+lookup"); + Cmd_RemoveCommand("-lookup"); + Cmd_RemoveCommand("+lookdown"); + Cmd_RemoveCommand("-lookdown"); + Cmd_RemoveCommand("+strafe"); + Cmd_RemoveCommand("-strafe"); + Cmd_RemoveCommand("+moveleft"); + Cmd_RemoveCommand("-moveleft"); + Cmd_RemoveCommand("+moveright"); + Cmd_RemoveCommand("-moveright"); + Cmd_RemoveCommand("+speed"); + Cmd_RemoveCommand("-speed"); + Cmd_RemoveCommand("+attack"); + Cmd_RemoveCommand("-attack"); + Cmd_RemoveCommand("+button0"); + Cmd_RemoveCommand("-button0"); + Cmd_RemoveCommand("+button1"); + Cmd_RemoveCommand("-button1"); + Cmd_RemoveCommand("+button2"); + Cmd_RemoveCommand("-button2"); + Cmd_RemoveCommand("+button3"); + Cmd_RemoveCommand("-button3"); + Cmd_RemoveCommand("+button4"); + Cmd_RemoveCommand("-button4"); + Cmd_RemoveCommand("+button5"); + Cmd_RemoveCommand("-button5"); + Cmd_RemoveCommand("+button6"); + Cmd_RemoveCommand("-button6"); + Cmd_RemoveCommand("+button7"); + Cmd_RemoveCommand("-button7"); + Cmd_RemoveCommand("+button8"); + Cmd_RemoveCommand("-button8"); + Cmd_RemoveCommand("+button9"); + Cmd_RemoveCommand("-button9"); + Cmd_RemoveCommand("+button10"); + Cmd_RemoveCommand("-button10"); + Cmd_RemoveCommand("+button11"); + Cmd_RemoveCommand("-button11"); + Cmd_RemoveCommand("+button12"); + Cmd_RemoveCommand("-button12"); + Cmd_RemoveCommand("+button13"); + Cmd_RemoveCommand("-button13"); + Cmd_RemoveCommand("+button14"); + Cmd_RemoveCommand("-button14"); + Cmd_RemoveCommand("+mlook"); + Cmd_RemoveCommand("-mlook"); + +#ifdef USE_VOIP + Cmd_RemoveCommand("+voiprecord"); + Cmd_RemoveCommand("-voiprecord"); +#endif +} -- cgit