mirror of
https://github.com/yquake2/yquake2remaster.git
synced 2024-12-14 06:21:27 +00:00
4ae8706d22
This is largely based upon the cl_async 1 mode from KMQuake2, which in turn is based upon r1q2. The origins of this code may be even older... Different to KMQuake2 the asynchonous mode is not optional, the client is always asynchonous. Since we're mainly integrating this rather fundamental change to simplify the complex internal timing between client, server and refresh, there's no point in keeping it optional. The old cl_maxfps cvar controls the network frames. 30 frames should be enough, even Q3A hasn't more. The new gl_maxfps cvar controls the render frames. It's set to 95 fps by default to avoid possible remnant of the famous 125hz bug.
855 lines
15 KiB
C
855 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 "../backends/generic/header/input.h"
|
|
|
|
cvar_t *cl_nodelta;
|
|
|
|
extern unsigned sys_frame_time;
|
|
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 (cmd->buttons && (cl.cinematictime > 0) && !cl.attractloop &&
|
|
(cls.realtime - cl.cinematictime > 1000))
|
|
{
|
|
/* 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));
|
|
}
|
|
|