mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-12-02 09:02:36 +00:00
bb0fc01e38
Until this commit a cinematic was aborted as soon as any key were marked down when finishing the user command and sending it to the server. The whole logic to detect if a key is down is broken, for example `vid_restart` may leave keys marked down that are in fact up. And there's the possibility to inject fake key events from nearly everywhere. I'm not really sure but I suspect that even the server may be able to inject key events. Therefore untangle the cinematic abort code from the user command processing, it should depend only on real key strokes: 1. Introduce a new global variable `abort_cinamatic` and set it to `cls.realtime` as soon as a key down event is detected. The only exceptions are Escape and Shift, because opening the menu and toggeling the console should never abort a cinematic. 2. When starting a cinematic `abort_cinamatic` is set to INT_MAX, because it needs to be higher than the current `cls.realtime`. 3. When a cinematic is running, `cls.key_dest` is set to `key_game` (`key_menu` and `key_console` are ignored, keys send to the menu or the console should never abort a cinematic; `key_message` can / should never happen while a cinematic is running) and `abort_cinamatic` is less than `cls.realtime` the cinematic is aborted. `abort_cinamatic` less than `cls.realtime` is necessary because the client needs one frame to pop up the menu or toggle the console and set the `cls.key_dest` accordingly. `abort_cinamatic == cls.realtime - 1` is not possible because not every frame finishes a user command. This closes #502.
854 lines
15 KiB
C
854 lines
15 KiB
C
/*
|
|
* Copyright (C) 1997-2001 Id Software, Inc.
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*
|
|
* =======================================================================
|
|
*
|
|
* This file implements the input handling like mouse events and
|
|
* keyboard strokes.
|
|
*
|
|
* =======================================================================
|
|
*/
|
|
|
|
#include "header/client.h"
|
|
#include "input/header/input.h"
|
|
|
|
cvar_t *cl_nodelta;
|
|
|
|
unsigned frame_msec;
|
|
unsigned old_sys_frame_time;
|
|
|
|
/*
|
|
* 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 a parameter to the command so it can be matched up with
|
|
* the release.
|
|
*
|
|
* state bit 0 is the current state of the key
|
|
* state bit 1 is edge triggered on the up to down transition
|
|
* state bit 2 is edge triggered on the down to up transition
|
|
*
|
|
*
|
|
* Key_Event (int key, qboolean down, unsigned time);
|
|
*
|
|
* +mlook src time
|
|
*/
|
|
|
|
kbutton_t in_klook;
|
|
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, in_use, in_attack;
|
|
kbutton_t in_up, in_down;
|
|
|
|
int in_impulse;
|
|
|
|
void
|
|
KeyDown(kbutton_t *b)
|
|
{
|
|
int k;
|
|
char *c;
|
|
|
|
c = Cmd_Argv(1);
|
|
|
|
if (c[0])
|
|
{
|
|
k = (int)strtol(c, (char **)NULL, 10);
|
|
}
|
|
|
|
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->state & 1)
|
|
{
|
|
return; /* still down */
|
|
}
|
|
|
|
/* save timestamp */
|
|
c = Cmd_Argv(2);
|
|
b->downtime = (int)strtol(c, (char **)NULL, 10);
|
|
|
|
if (!b->downtime)
|
|
{
|
|
b->downtime = sys_frame_time - 100;
|
|
}
|
|
|
|
b->state |= 1 + 2; /* down + impulse down */
|
|
}
|
|
|
|
void
|
|
KeyUp(kbutton_t *b)
|
|
{
|
|
int k;
|
|
char *c;
|
|
unsigned uptime;
|
|
|
|
c = Cmd_Argv(1);
|
|
|
|
if (c[0])
|
|
{
|
|
k = (int)strtol(c, (char **)NULL, 10);
|
|
}
|
|
|
|
else
|
|
{
|
|
/* typed manually at the console, assume for unsticking, so clear all */
|
|
b->down[0] = b->down[1] = 0;
|
|
b->state = 4; /* impulse up */
|
|
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 */
|
|
}
|
|
|
|
if (!(b->state & 1))
|
|
{
|
|
return; /* still up (this should not happen) */
|
|
}
|
|
|
|
/* save timestamp */
|
|
c = Cmd_Argv(2);
|
|
uptime = (int)strtol(c, (char **)NULL, 10);
|
|
|
|
if (uptime)
|
|
{
|
|
b->msec += uptime - b->downtime;
|
|
}
|
|
|
|
else
|
|
{
|
|
b->msec += 10;
|
|
}
|
|
|
|
b->state &= ~1; /* now up */
|
|
b->state |= 4; /* impulse up */
|
|
}
|
|
|
|
void
|
|
IN_KLookDown(void)
|
|
{
|
|
KeyDown(&in_klook);
|
|
}
|
|
|
|
void
|
|
IN_KLookUp(void)
|
|
{
|
|
KeyUp(&in_klook);
|
|
}
|
|
|
|
void
|
|
IN_UpDown(void)
|
|
{
|
|
KeyDown(&in_up);
|
|
}
|
|
|
|
void
|
|
IN_UpUp(void)
|
|
{
|
|
KeyUp(&in_up);
|
|
}
|
|
|
|
void
|
|
IN_DownDown(void)
|
|
{
|
|
KeyDown(&in_down);
|
|
}
|
|
|
|
void
|
|
IN_DownUp(void)
|
|
{
|
|
KeyUp(&in_down);
|
|
}
|
|
|
|
void
|
|
IN_LeftDown(void)
|
|
{
|
|
KeyDown(&in_left);
|
|
}
|
|
|
|
void
|
|
IN_LeftUp(void)
|
|
{
|
|
KeyUp(&in_left);
|
|
}
|
|
|
|
void
|
|
IN_RightDown(void)
|
|
{
|
|
KeyDown(&in_right);
|
|
}
|
|
|
|
void
|
|
IN_RightUp(void)
|
|
{
|
|
KeyUp(&in_right);
|
|
}
|
|
|
|
void
|
|
IN_ForwardDown(void)
|
|
{
|
|
KeyDown(&in_forward);
|
|
}
|
|
|
|
void
|
|
IN_ForwardUp(void)
|
|
{
|
|
KeyUp(&in_forward);
|
|
}
|
|
|
|
void
|
|
IN_BackDown(void)
|
|
{
|
|
KeyDown(&in_back);
|
|
}
|
|
|
|
void
|
|
IN_BackUp(void)
|
|
{
|
|
KeyUp(&in_back);
|
|
}
|
|
|
|
void
|
|
IN_LookupDown(void)
|
|
{
|
|
KeyDown(&in_lookup);
|
|
}
|
|
|
|
void
|
|
IN_LookupUp(void)
|
|
{
|
|
KeyUp(&in_lookup);
|
|
}
|
|
|
|
void
|
|
IN_LookdownDown(void)
|
|
{
|
|
KeyDown(&in_lookdown);
|
|
}
|
|
|
|
void
|
|
IN_LookdownUp(void)
|
|
{
|
|
KeyUp(&in_lookdown);
|
|
}
|
|
|
|
void
|
|
IN_MoveleftDown(void)
|
|
{
|
|
KeyDown(&in_moveleft);
|
|
}
|
|
|
|
void
|
|
IN_MoveleftUp(void)
|
|
{
|
|
KeyUp(&in_moveleft);
|
|
}
|
|
|
|
void
|
|
IN_MoverightDown(void)
|
|
{
|
|
KeyDown(&in_moveright);
|
|
}
|
|
|
|
void
|
|
IN_MoverightUp(void)
|
|
{
|
|
KeyUp(&in_moveright);
|
|
}
|
|
|
|
void
|
|
IN_SpeedDown(void)
|
|
{
|
|
KeyDown(&in_speed);
|
|
}
|
|
|
|
void
|
|
IN_SpeedUp(void)
|
|
{
|
|
KeyUp(&in_speed);
|
|
}
|
|
|
|
void
|
|
IN_StrafeDown(void)
|
|
{
|
|
KeyDown(&in_strafe);
|
|
}
|
|
|
|
void
|
|
IN_StrafeUp(void)
|
|
{
|
|
KeyUp(&in_strafe);
|
|
}
|
|
|
|
void
|
|
IN_AttackDown(void)
|
|
{
|
|
KeyDown(&in_attack);
|
|
}
|
|
|
|
void
|
|
IN_AttackUp(void)
|
|
{
|
|
KeyUp(&in_attack);
|
|
}
|
|
|
|
void
|
|
IN_UseDown(void)
|
|
{
|
|
KeyDown(&in_use);
|
|
}
|
|
|
|
void
|
|
IN_UseUp(void)
|
|
{
|
|
KeyUp(&in_use);
|
|
}
|
|
|
|
void
|
|
IN_Impulse(void)
|
|
{
|
|
in_impulse = (int)strtol(Cmd_Argv(1), (char **)NULL, 10);
|
|
}
|
|
|
|
/*
|
|
* Returns the fraction of the
|
|
* frame that the key was down
|
|
*/
|
|
float
|
|
CL_KeyState(kbutton_t *key)
|
|
{
|
|
float val;
|
|
int msec;
|
|
|
|
key->state &= 1; /* clear impulses */
|
|
|
|
msec = key->msec;
|
|
key->msec = 0;
|
|
|
|
if (key->state)
|
|
{
|
|
/* still down */
|
|
msec += sys_frame_time - key->downtime;
|
|
key->downtime = sys_frame_time;
|
|
}
|
|
|
|
val = (float)msec / frame_msec;
|
|
|
|
if (val < 0)
|
|
{
|
|
val = 0;
|
|
}
|
|
|
|
if (val > 1)
|
|
{
|
|
val = 1;
|
|
}
|
|
|
|
return val;
|
|
}
|
|
|
|
cvar_t *cl_upspeed;
|
|
cvar_t *cl_forwardspeed;
|
|
cvar_t *cl_sidespeed;
|
|
cvar_t *cl_yawspeed;
|
|
cvar_t *cl_pitchspeed;
|
|
cvar_t *cl_run;
|
|
cvar_t *cl_anglespeedkey;
|
|
|
|
/*
|
|
* Moves the local angle positions
|
|
*/
|
|
void
|
|
CL_AdjustAngles(void)
|
|
{
|
|
float speed;
|
|
float up, down;
|
|
|
|
if (in_speed.state & 1)
|
|
{
|
|
speed = cls.nframetime * cl_anglespeedkey->value;
|
|
}
|
|
|
|
else
|
|
{
|
|
speed = cls.nframetime;
|
|
}
|
|
|
|
if (!(in_strafe.state & 1))
|
|
{
|
|
cl.viewangles[YAW] -= speed * cl_yawspeed->value * CL_KeyState(&in_right);
|
|
cl.viewangles[YAW] += speed * cl_yawspeed->value * CL_KeyState(&in_left);
|
|
}
|
|
|
|
if (in_klook.state & 1)
|
|
{
|
|
cl.viewangles[PITCH] -= speed * cl_pitchspeed->value * CL_KeyState(&in_forward);
|
|
cl.viewangles[PITCH] += speed * cl_pitchspeed->value * CL_KeyState(&in_back);
|
|
}
|
|
|
|
up = CL_KeyState(&in_lookup);
|
|
down = CL_KeyState(&in_lookdown);
|
|
|
|
cl.viewangles[PITCH] -= speed * cl_pitchspeed->value * up;
|
|
cl.viewangles[PITCH] += speed * cl_pitchspeed->value * down;
|
|
}
|
|
|
|
/*
|
|
* Send the intended movement message to the server
|
|
*/
|
|
void
|
|
CL_BaseMove(usercmd_t *cmd)
|
|
{
|
|
CL_AdjustAngles();
|
|
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
|
|
VectorCopy(cl.viewangles, cmd->angles);
|
|
|
|
if (in_strafe.state & 1)
|
|
{
|
|
cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_right);
|
|
cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_left);
|
|
}
|
|
|
|
cmd->sidemove += cl_sidespeed->value * CL_KeyState(&in_moveright);
|
|
cmd->sidemove -= cl_sidespeed->value * CL_KeyState(&in_moveleft);
|
|
|
|
cmd->upmove += cl_upspeed->value * CL_KeyState(&in_up);
|
|
cmd->upmove -= cl_upspeed->value * CL_KeyState(&in_down);
|
|
|
|
if (!(in_klook.state & 1))
|
|
{
|
|
cmd->forwardmove += cl_forwardspeed->value * CL_KeyState(&in_forward);
|
|
cmd->forwardmove -= cl_forwardspeed->value * CL_KeyState(&in_back);
|
|
}
|
|
|
|
/* adjust for speed key / running */
|
|
if ((in_speed.state & 1) ^ (int)(cl_run->value))
|
|
{
|
|
cmd->forwardmove *= 2;
|
|
cmd->sidemove *= 2;
|
|
cmd->upmove *= 2;
|
|
}
|
|
}
|
|
|
|
void
|
|
CL_ClampPitch(void)
|
|
{
|
|
float pitch;
|
|
|
|
pitch = SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
|
|
|
|
if (pitch > 180)
|
|
{
|
|
pitch -= 360;
|
|
}
|
|
|
|
if (cl.viewangles[PITCH] + pitch < -360)
|
|
{
|
|
cl.viewangles[PITCH] += 360; /* wrapped */
|
|
}
|
|
|
|
if (cl.viewangles[PITCH] + pitch > 360)
|
|
{
|
|
cl.viewangles[PITCH] -= 360; /* wrapped */
|
|
}
|
|
|
|
if (cl.viewangles[PITCH] + pitch > 89)
|
|
{
|
|
cl.viewangles[PITCH] = 89 - pitch;
|
|
}
|
|
|
|
if (cl.viewangles[PITCH] + pitch < -89)
|
|
{
|
|
cl.viewangles[PITCH] = -89 - pitch;
|
|
}
|
|
}
|
|
|
|
void
|
|
CL_FinishMove(usercmd_t *cmd)
|
|
{
|
|
int ms;
|
|
int i;
|
|
|
|
/* figure button bits */
|
|
if (in_attack.state & 3)
|
|
{
|
|
cmd->buttons |= BUTTON_ATTACK;
|
|
}
|
|
|
|
in_attack.state &= ~2;
|
|
|
|
if (in_use.state & 3)
|
|
{
|
|
cmd->buttons |= BUTTON_USE;
|
|
}
|
|
|
|
in_use.state &= ~2;
|
|
|
|
if (anykeydown && (cls.key_dest == key_game))
|
|
{
|
|
cmd->buttons |= BUTTON_ANY;
|
|
}
|
|
|
|
/* send milliseconds of time to apply the move */
|
|
ms = cls.nframetime * 1000;
|
|
|
|
if (ms > 250)
|
|
{
|
|
ms = 100; /* time was unreasonable */
|
|
}
|
|
|
|
cmd->msec = ms;
|
|
|
|
CL_ClampPitch();
|
|
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
cmd->angles[i] = ANGLE2SHORT(cl.viewangles[i]);
|
|
}
|
|
|
|
cmd->impulse = in_impulse;
|
|
in_impulse = 0;
|
|
|
|
/* send the ambient light level at the player's current position */
|
|
cmd->lightlevel = (byte)cl_lightlevel->value;
|
|
}
|
|
|
|
void
|
|
IN_CenterView(void)
|
|
{
|
|
cl.viewangles[PITCH] = -SHORT2ANGLE(cl.frame.playerstate.pmove.delta_angles[PITCH]);
|
|
}
|
|
|
|
/*
|
|
* Centers the view
|
|
*/
|
|
static void
|
|
IN_ForceCenterView(void)
|
|
{
|
|
cl.viewangles[PITCH] = 0;
|
|
}
|
|
|
|
void
|
|
CL_InitInput(void)
|
|
{
|
|
Cmd_AddCommand("centerview", IN_CenterView);
|
|
Cmd_AddCommand("force_centerview", IN_ForceCenterView);
|
|
|
|
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_AttackDown);
|
|
Cmd_AddCommand("-attack", IN_AttackUp);
|
|
Cmd_AddCommand("+use", IN_UseDown);
|
|
Cmd_AddCommand("-use", IN_UseUp);
|
|
Cmd_AddCommand("impulse", IN_Impulse);
|
|
Cmd_AddCommand("+klook", IN_KLookDown);
|
|
Cmd_AddCommand("-klook", IN_KLookUp);
|
|
|
|
cl_nodelta = Cvar_Get("cl_nodelta", "0", 0);
|
|
}
|
|
|
|
void
|
|
CL_RefreshCmd(void)
|
|
{
|
|
int ms;
|
|
usercmd_t *cmd;
|
|
|
|
// CMD to fill
|
|
cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
|
|
|
|
// Calculate delta
|
|
frame_msec = sys_frame_time - old_sys_frame_time;
|
|
|
|
// Check bounds
|
|
if (frame_msec < 1)
|
|
{
|
|
return;
|
|
}
|
|
else if (frame_msec > 200)
|
|
{
|
|
frame_msec = 200;
|
|
}
|
|
|
|
// Add movement
|
|
CL_BaseMove(cmd);
|
|
IN_Move(cmd);
|
|
|
|
// Clamp angels for prediction
|
|
CL_ClampPitch();
|
|
|
|
cmd->angles[0] = ANGLE2SHORT(cl.viewangles[0]);
|
|
cmd->angles[1] = ANGLE2SHORT(cl.viewangles[1]);
|
|
cmd->angles[2] = ANGLE2SHORT(cl.viewangles[2]);
|
|
|
|
// Update time for prediction
|
|
ms = (int)(cls.nframetime * 1000.0f);
|
|
|
|
if (ms > 250)
|
|
{
|
|
ms = 100;
|
|
}
|
|
|
|
cmd->msec = ms;
|
|
|
|
// Update frame time for the next call
|
|
old_sys_frame_time = sys_frame_time;
|
|
|
|
// Important events are send immediately
|
|
if (((in_attack.state & 2)) || (in_use.state & 2))
|
|
{
|
|
cls.forcePacket = true;
|
|
}
|
|
}
|
|
|
|
void
|
|
CL_RefreshMove(void)
|
|
{
|
|
usercmd_t *cmd;
|
|
|
|
// CMD to fill
|
|
cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
|
|
|
|
// Calculate delta
|
|
frame_msec = sys_frame_time - old_sys_frame_time;
|
|
|
|
// Check bounds
|
|
if (frame_msec < 1)
|
|
{
|
|
return;
|
|
}
|
|
else if (frame_msec > 200)
|
|
{
|
|
frame_msec = 200;
|
|
}
|
|
|
|
// Add movement
|
|
CL_BaseMove(cmd);
|
|
IN_Move(cmd);
|
|
|
|
old_sys_frame_time = sys_frame_time;
|
|
}
|
|
|
|
void
|
|
CL_FinalizeCmd(void)
|
|
{
|
|
usercmd_t *cmd;
|
|
|
|
// CMD to fill
|
|
cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
|
|
|
|
// Mouse button events
|
|
if (in_attack.state & 3)
|
|
{
|
|
cmd->buttons |= BUTTON_ATTACK;
|
|
}
|
|
|
|
in_attack.state &= ~2;
|
|
|
|
if (in_use.state & 3)
|
|
{
|
|
cmd->buttons |= BUTTON_USE;
|
|
}
|
|
|
|
in_use.state &= ~2;
|
|
|
|
// Keyboard events
|
|
if (anykeydown && cls.key_dest == key_game)
|
|
{
|
|
cmd->buttons |= BUTTON_ANY;
|
|
}
|
|
|
|
cmd->impulse = in_impulse;
|
|
in_impulse = 0;
|
|
|
|
// Set light level for muzzle flash
|
|
cmd->lightlevel = (byte)cl_lightlevel->value;
|
|
}
|
|
|
|
void
|
|
CL_SendCmd(void)
|
|
{
|
|
sizebuf_t buf;
|
|
byte data[128];
|
|
int i;
|
|
usercmd_t *cmd, *oldcmd;
|
|
usercmd_t nullcmd;
|
|
int checksumIndex;
|
|
|
|
memset(&buf, 0, sizeof(buf));
|
|
|
|
/* save this command off for prediction */
|
|
i = cls.netchan.outgoing_sequence & (CMD_BACKUP - 1);
|
|
cmd = &cl.cmds[i];
|
|
cl.cmd_time[i] = cls.realtime; /* for netgraph ping calculation */
|
|
|
|
CL_FinalizeCmd();
|
|
|
|
cl.cmd = *cmd;
|
|
|
|
if ((cls.state == ca_disconnected) || (cls.state == ca_connecting))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (cls.state == ca_connected)
|
|
{
|
|
if (cls.netchan.message.cursize || (curtime - cls.netchan.last_sent > 1000))
|
|
{
|
|
Netchan_Transmit(&cls.netchan, 0, buf.data);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/* send a userinfo update if needed */
|
|
if (userinfo_modified)
|
|
{
|
|
CL_FixUpGender();
|
|
userinfo_modified = false;
|
|
MSG_WriteByte(&cls.netchan.message, clc_userinfo);
|
|
MSG_WriteString(&cls.netchan.message, Cvar_Userinfo());
|
|
}
|
|
|
|
SZ_Init(&buf, data, sizeof(data));
|
|
|
|
if ((cls.realtime > abort_cinematic) && (cl.cinematictime > 0) &&
|
|
!cl.attractloop && (cls.realtime - cl.cinematictime > 1000) &&
|
|
(cls.key_dest == key_game))
|
|
{
|
|
/* skip the rest of the cinematic */
|
|
SCR_FinishCinematic();
|
|
}
|
|
|
|
/* begin a client move command */
|
|
MSG_WriteByte(&buf, clc_move);
|
|
|
|
/* save the position for a checksum byte */
|
|
checksumIndex = buf.cursize;
|
|
MSG_WriteByte(&buf, 0);
|
|
|
|
/* let the server know what the last frame we
|
|
got was, so the next message can be delta
|
|
compressed */
|
|
if (cl_nodelta->value || !cl.frame.valid || cls.demowaiting)
|
|
{
|
|
MSG_WriteLong(&buf, -1); /* no compression */
|
|
}
|
|
else
|
|
{
|
|
MSG_WriteLong(&buf, cl.frame.serverframe);
|
|
}
|
|
|
|
/* send this and the previous cmds in the message, so
|
|
if the last packet was dropped, it can be recovered */
|
|
i = (cls.netchan.outgoing_sequence - 2) & (CMD_BACKUP - 1);
|
|
cmd = &cl.cmds[i];
|
|
memset(&nullcmd, 0, sizeof(nullcmd));
|
|
MSG_WriteDeltaUsercmd(&buf, &nullcmd, cmd);
|
|
oldcmd = cmd;
|
|
|
|
i = (cls.netchan.outgoing_sequence - 1) & (CMD_BACKUP - 1);
|
|
cmd = &cl.cmds[i];
|
|
MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
|
|
oldcmd = cmd;
|
|
|
|
i = (cls.netchan.outgoing_sequence) & (CMD_BACKUP - 1);
|
|
cmd = &cl.cmds[i];
|
|
MSG_WriteDeltaUsercmd(&buf, oldcmd, cmd);
|
|
|
|
/* calculate a checksum over the move commands */
|
|
buf.data[checksumIndex] = COM_BlockSequenceCRCByte(
|
|
buf.data + checksumIndex + 1, buf.cursize - checksumIndex - 1,
|
|
cls.netchan.outgoing_sequence);
|
|
|
|
/* deliver the message */
|
|
Netchan_Transmit(&cls.netchan, buf.cursize, buf.data);
|
|
|
|
/* Reinit the current cmd buffer */
|
|
cmd = &cl.cmds[cls.netchan.outgoing_sequence & (CMD_BACKUP - 1)];
|
|
memset(cmd, 0, sizeof(*cmd));
|
|
}
|
|
|