// if we couldn't find any of the weapons, there's nothing more we can do...
}
+#if 0
void IN_CycleWeapon (void)
{
int i, n;
}
// if we couldn't find any of the weapons, there's nothing more we can do...
}
+#endif
/*
===============
float val;
qboolean impulsedown, impulseup, down;
- impulsedown = key->state & 2;
- impulseup = key->state & 4;
- down = key->state & 1;
+ impulsedown = (key->state & 2) != 0;
+ impulseup = (key->state & 4) != 0;
+ down = (key->state & 1) != 0;
val = 0;
if (impulsedown && !impulseup)
cvar_t cl_sidespeed = {CVAR_SAVE, "cl_sidespeed","350","strafe movement speed"};
cvar_t cl_movespeedkey = {CVAR_SAVE, "cl_movespeedkey","2.0","how much +speed multiplies keyboard movement speed"};
+cvar_t cl_movecliptokeyboard = {0, "cl_movecliptokeyboard", "0", "if set to 1, any move is clipped to the nine keyboard states; if set to 2, only the direction is clipped, not the amount"};
cvar_t cl_yawspeed = {CVAR_SAVE, "cl_yawspeed","140","keyboard yaw turning speed"};
cvar_t cl_pitchspeed = {CVAR_SAVE, "cl_pitchspeed","150","keyboard pitch turning speed"};
cvar_t cl_anglespeedkey = {CVAR_SAVE, "cl_anglespeedkey","1.5","how much +speed multiplies keyboard turning speed"};
cvar_t cl_movement = {CVAR_SAVE, "cl_movement", "0", "enables clientside prediction of your player movement"};
+cvar_t cl_movement_nettimeout = {CVAR_SAVE, "cl_movement_nettimeout", "0.3", "stops predicting moves when server is lagging badly (avoids major performance problems), timeout in seconds"};
cvar_t cl_movement_minping = {CVAR_SAVE, "cl_movement_minping", "0", "whether to use prediction when ping is lower than this value in milliseconds"};
cvar_t cl_movement_track_canjump = {CVAR_SAVE, "cl_movement_track_canjump", "1", "track if the player released the jump key between two jumps to decide if he is able to jump or not; when off, this causes some \"sliding\" slightly above the floor when the jump key is held too long; if the mod allows repeated jumping by holding space all the time, this has to be set to zero too"};
cvar_t cl_movement_maxspeed = {0, "cl_movement_maxspeed", "320", "how fast you can move (should match sv_maxspeed)"};
// clamp after the move to prevent rendering with bad angles
CL_AdjustAngles ();
+
+ if(cl_movecliptokeyboard.integer)
+ {
+ vec_t f = 1;
+ if (in_speed.state & 1)
+ f *= cl_movespeedkey.value;
+ if(cl_movecliptokeyboard.integer == 2)
+ {
+ // digital direction, analog amount
+ vec_t wishvel_x, wishvel_y;
+ f *= max(cl_sidespeed.value, max(cl_forwardspeed.value, cl_backspeed.value));
+ wishvel_x = fabs(cl.cmd.forwardmove);
+ wishvel_y = fabs(cl.cmd.sidemove);
+ if(wishvel_x != 0 && wishvel_y != 0 && wishvel_x != wishvel_y)
+ {
+ vec_t wishspeed = sqrt(wishvel_x * wishvel_x + wishvel_y * wishvel_y);
+ if(wishvel_x >= 2 * wishvel_y)
+ {
+ // pure X motion
+ if(cl.cmd.forwardmove > 0)
+ cl.cmd.forwardmove = wishspeed;
+ else
+ cl.cmd.forwardmove = -wishspeed;
+ cl.cmd.sidemove = 0;
+ }
+ else if(wishvel_y >= 2 * wishvel_x)
+ {
+ // pure Y motion
+ cl.cmd.forwardmove = 0;
+ if(cl.cmd.sidemove > 0)
+ cl.cmd.sidemove = wishspeed;
+ else
+ cl.cmd.sidemove = -wishspeed;
+ }
+ else
+ {
+ // diagonal
+ if(cl.cmd.forwardmove > 0)
+ cl.cmd.forwardmove = 0.70710678118654752440 * wishspeed;
+ else
+ cl.cmd.forwardmove = -0.70710678118654752440 * wishspeed;
+ if(cl.cmd.sidemove > 0)
+ cl.cmd.sidemove = 0.70710678118654752440 * wishspeed;
+ else
+ cl.cmd.sidemove = -0.70710678118654752440 * wishspeed;
+ }
+ }
+ }
+ else if(cl_movecliptokeyboard.integer)
+ {
+ // digital direction, digital amount
+ if(cl.cmd.sidemove >= cl_sidespeed.value * f * 0.5)
+ cl.cmd.sidemove = cl_sidespeed.value * f;
+ else if(cl.cmd.sidemove <= -cl_sidespeed.value * f * 0.5)
+ cl.cmd.sidemove = -cl_sidespeed.value * f;
+ else
+ cl.cmd.sidemove = 0;
+ if(cl.cmd.forwardmove >= cl_forwardspeed.value * f * 0.5)
+ cl.cmd.forwardmove = cl_forwardspeed.value * f;
+ else if(cl.cmd.forwardmove <= -cl_backspeed.value * f * 0.5)
+ cl.cmd.forwardmove = -cl_backspeed.value * f;
+ else
+ cl.cmd.forwardmove = 0;
+ }
+ }
}
#include "cl_collision.h"
for (i = 0;i < NUMOFFSETS;i++)
{
VectorAdd(offsets[i], s->origin, neworigin);
- if (!CL_Move(neworigin, cl.playercrouchmins, cl.playercrouchmaxs, neworigin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false).startsolid)
+ if (!CL_TraceBox(neworigin, cl.playercrouchmins, cl.playercrouchmaxs, neworigin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false).startsolid)
{
VectorCopy(neworigin, s->origin);
return true;
// low ceiling first
if (s->crouched)
{
- trace = CL_Move(s->origin, cl.playerstandmins, cl.playerstandmaxs, s->origin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ trace = CL_TraceBox(s->origin, cl.playerstandmins, cl.playerstandmaxs, s->origin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
if (!trace.startsolid)
s->crouched = false;
}
// set onground
VectorSet(origin1, s->origin[0], s->origin[1], s->origin[2] + 1);
VectorSet(origin2, s->origin[0], s->origin[1], s->origin[2] - 1); // -2 causes clientside doublejump bug at above 150fps, raising that to 300fps :)
- trace = CL_Move(origin1, s->mins, s->maxs, origin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ trace = CL_TraceBox(origin1, s->mins, s->maxs, origin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
s->onground = trace.fraction < 1 && trace.plane.normal[2] > 0.7;
// set watertype/waterlevel
VectorSet(origin1, s->origin[0], s->origin[1], s->origin[2] + s->mins[2] + 1);
s->waterlevel = WATERLEVEL_NONE;
- s->watertype = CL_Move(origin1, vec3_origin, vec3_origin, origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK;
+ s->watertype = CL_TracePoint(origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK;
if (s->watertype)
{
s->waterlevel = WATERLEVEL_WETFEET;
origin1[2] = s->origin[2] + (s->mins[2] + s->maxs[2]) * 0.5f;
- if (CL_Move(origin1, vec3_origin, vec3_origin, origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
+ if (CL_TracePoint(origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
{
s->waterlevel = WATERLEVEL_SWIMMING;
origin1[2] = s->origin[2] + 22;
- if (CL_Move(origin1, vec3_origin, vec3_origin, origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
+ if (CL_TracePoint(origin1, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsupercontents & SUPERCONTENTS_LIQUIDSMASK)
s->waterlevel = WATERLEVEL_SUBMERGED;
}
}
for (bump = 0, t = s->cmd.frametime;bump < 8 && VectorLength2(s->velocity) > 0;bump++)
{
VectorMA(s->origin, t, s->velocity, neworigin);
- trace = CL_Move(s->origin, s->mins, s->maxs, neworigin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ trace = CL_TraceBox(s->origin, s->mins, s->maxs, neworigin, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
if (trace.fraction < 1 && trace.plane.normal[2] == 0)
{
// may be a step or wall, try stepping up
// first move forward at a higher level
VectorSet(currentorigin2, s->origin[0], s->origin[1], s->origin[2] + cl.movevars_stepheight);
VectorSet(neworigin2, neworigin[0], neworigin[1], s->origin[2] + cl.movevars_stepheight);
- trace2 = CL_Move(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ trace2 = CL_TraceBox(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
if (!trace2.startsolid)
{
// then move down from there
VectorCopy(trace2.endpos, currentorigin2);
VectorSet(neworigin2, trace2.endpos[0], trace2.endpos[1], s->origin[2]);
- trace3 = CL_Move(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ trace3 = CL_TraceBox(currentorigin2, s->mins, s->maxs, neworigin2, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
//Con_Printf("%f %f %f %f : %f %f %f %f : %f %f %f %f\n", trace.fraction, trace.endpos[0], trace.endpos[1], trace.endpos[2], trace2.fraction, trace2.endpos[0], trace2.endpos[1], trace2.endpos[2], trace3.fraction, trace3.endpos[0], trace3.endpos[1], trace3.endpos[2]);
// accept the new trace if it made some progress
if (fabs(trace3.endpos[0] - trace.endpos[0]) >= 0.03125 || fabs(trace3.endpos[1] - trace.endpos[1]) >= 0.03125)
AngleVectors(yawangles, forward, NULL, NULL);
VectorMA(s->origin, 24, forward, spot);
spot[2] += 8;
- if (CL_Move(spot, vec3_origin, vec3_origin, spot, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsolid)
+ if (CL_TracePoint(spot, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsolid)
{
spot[2] += 24;
- if (!CL_Move(spot, vec3_origin, vec3_origin, spot, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsolid)
+ if (!CL_TracePoint(spot, MOVE_NOMONSTERS, NULL, 0, true, false, NULL, false).startsolid)
{
VectorScale(forward, 50, s->velocity);
s->velocity[2] = 310;
s->velocity[2] = zspeed;
}
-void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed, vec_t accel, vec_t accelqw, vec_t sidefric)
+void CL_ClientMovement_Physics_PM_Accelerate(cl_clientmovement_state_t *s, vec3_t wishdir, vec_t wishspeed, vec_t wishspeed0, vec_t accel, vec_t accelqw, vec_t sidefric)
{
vec_t vel_straight, vel_z;
vec3_t vel_perpend;
vec_t addspeed;
+ vec_t savespeed;
+
+ if(cl.moveflags & MOVEFLAG_Q2AIRACCELERATE)
+ wishspeed0 = wishspeed; // don't need to emulate this Q1 bug
+
+ savespeed = VectorLength2(s->velocity);
vel_straight = DotProduct(s->velocity, wishdir);
vel_z = s->velocity[2];
addspeed = wishspeed - vel_straight;
if(addspeed > 0)
- vel_straight = vel_straight + min(addspeed, accel * s->cmd.frametime * wishspeed) * accelqw;
+ vel_straight = vel_straight + min(addspeed, accel * s->cmd.frametime * wishspeed0) * accelqw;
if(wishspeed > 0)
- vel_straight = vel_straight + min(wishspeed, accel * s->cmd.frametime * wishspeed) * (1 - accelqw);
+ vel_straight = vel_straight + min(wishspeed, accel * s->cmd.frametime * wishspeed0) * (1 - accelqw);
- VectorScale(vel_perpend, 1 - s->cmd.frametime * wishspeed * sidefric, vel_perpend);
+ if(sidefric < 0 && VectorLength2(vel_perpend))
+ {
+ vec_t f, fmin;
+ f = 1 - s->cmd.frametime * wishspeed * sidefric;
+ fmin = (savespeed - vel_straight*vel_straight) / VectorLength2(vel_perpend);
+ if(fmin <= 0)
+ VectorScale(vel_perpend, f, vel_perpend);
+ else
+ VectorScale(vel_perpend, min(1.0f, max(fmin, f)), vel_perpend);
+ }
+ else
+ VectorScale(vel_perpend, 1 - s->cmd.frametime * wishspeed * sidefric, vel_perpend);
VectorMA(vel_perpend, vel_straight, wishdir, s->velocity);
s->velocity[2] += vel_z;
VectorSet(neworigin2, s->origin[0] + s->velocity[0]*(16/f), s->origin[1] + s->velocity[1]*(16/f), s->origin[2] + s->mins[2]);
VectorSet(neworigin3, neworigin2[0], neworigin2[1], neworigin2[2] - 34);
if (cls.protocol == PROTOCOL_QUAKEWORLD)
- trace = CL_Move(neworigin2, s->mins, s->maxs, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ trace = CL_TraceBox(neworigin2, s->mins, s->maxs, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
else
- trace = CL_Move(neworigin2, vec3_origin, vec3_origin, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
+ trace = CL_TraceLine(neworigin2, neworigin3, MOVE_NORMAL, NULL, SUPERCONTENTS_SOLID | SUPERCONTENTS_BODY | SUPERCONTENTS_PLAYERCLIP, true, true, NULL, false);
if (trace.fraction == 1 && !trace.startsolid)
friction *= cl.movevars_edgefriction;
}
if (s->waterjumptime <= 0)
{
// apply air speed limit
- vec_t accel, wishspeed2, accelqw;
+ vec_t accel, wishspeed0, wishspeed2, accelqw;
qboolean accelerating;
accelqw = cl.movevars_airaccel_qw;
+ wishspeed0 = wishspeed;
wishspeed = min(wishspeed, cl.movevars_maxairspeed);
if (s->crouched)
wishspeed *= 0.5;
if(cl.movevars_warsowbunny_turnaccel && accelerating && s->cmd.sidemove == 0 && s->cmd.forwardmove != 0)
CL_ClientMovement_Physics_PM_AirAccelerate(s, wishdir, wishspeed2);
else
- CL_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, accel, accelqw, cl.movevars_airaccel_sideways_friction / cl.movevars_maxairspeed);
+ CL_ClientMovement_Physics_PM_Accelerate(s, wishdir, wishspeed, wishspeed0, accel, accelqw, cl.movevars_airaccel_sideways_friction / cl.movevars_maxairspeed);
if(cl.movevars_aircontrol)
CL_ClientMovement_Physics_CPM_PM_Aircontrol(s, wishdir, wishspeed2);
{
if (cls.protocol == PROTOCOL_QUAKEWORLD)
{
+ cl.moveflags = 0;
}
else if (cl.stats[STAT_MOVEVARS_TICRATE])
{
+ cl.moveflags = cl.stats[STAT_MOVEFLAGS];
+ cl.movevars_ticrate = cl.statsf[STAT_MOVEVARS_TICRATE];
cl.movevars_timescale = cl.statsf[STAT_MOVEVARS_TIMESCALE];
cl.movevars_gravity = cl.statsf[STAT_MOVEVARS_GRAVITY];
cl.movevars_stopspeed = cl.statsf[STAT_MOVEVARS_STOPSPEED] ;
}
else
{
+ cl.moveflags = 0;
+ cl.movevars_ticrate = slowmo.value / bound(1.0f, cl_netfps.value, 1000.0f);
cl.movevars_timescale = slowmo.value;
cl.movevars_gravity = sv_gravity.value;
cl.movevars_stopspeed = cl_movement_stopspeed.value;
cl.movevars_warsowbunny_turnaccel = 0;
cl.movevars_warsowbunny_backtosideratio = 0;
}
+
+ if(!(cl.moveflags & MOVEFLAG_VALID))
+ {
+ if(gamemode == GAME_NEXUIZ)
+ cl.moveflags = MOVEFLAG_Q2AIRACCELERATE;
+ }
}
void CL_ClientMovement_Replay(void)
s.cmd.canjump = cl.movecmd[i+1].canjump;
// if a move is more than 50ms, do it as two moves (matching qwsv)
//Con_Printf("%i ", s.cmd.msec);
- if (s.cmd.frametime > 0.05)
+ if(s.cmd.frametime > 0.0005)
{
- s.cmd.frametime /= 2;
+ if (s.cmd.frametime > 0.05)
+ {
+ s.cmd.frametime /= 2;
+ CL_ClientMovement_PlayerMove(&s);
+ }
CL_ClientMovement_PlayerMove(&s);
+ cl.movecmd[i].canjump = s.cmd.canjump;
}
- CL_ClientMovement_PlayerMove(&s);
- cl.movecmd[i].canjump = s.cmd.canjump;
}
//Con_Printf("\n");
+ CL_ClientMovement_UpdateStatus(&s);
}
else
{
// get the first movement queue entry to know whether to crouch and such
s.cmd = cl.movecmd[0];
}
- CL_ClientMovement_UpdateStatus(&s);
if (cls.demoplayback) // for bob, speedometer
VectorCopy(cl.mvelocity[0], cl.movement_velocity);
MSG_WriteByte(buf, to->msec);
}
+void CL_NewFrameReceived(int num)
+{
+ if (developer_networkentities.integer >= 10)
+ Con_Printf("recv: svc_entities %i\n", num);
+ cl.latestframenums[cl.latestframenumsposition] = num;
+ cl.latestsendnums[cl.latestframenumsposition] = cl.cmd.sequence;
+ cl.latestframenumsposition = (cl.latestframenumsposition + 1) % LATESTFRAMENUMS;
+}
+
/*
==============
CL_SendMove
unsigned char data[1024];
double packettime;
int msecdelta;
+ qboolean quemove;
+ qboolean important;
// if playing a demo, do nothing
if (!cls.netcon)
return;
- // we build up cl.movecmd[0] and then decide whether to send or not
- // the prediction code will use this command even though it has not been
- // sent yet
+ // we don't que moves during a lag spike (potential network timeout)
+ quemove = realtime - cl.last_received_message < cl_movement_nettimeout.value;
+
+ // we build up cl.cmd and then decide whether to send or not
+ // we store this into cl.movecmd[0] for prediction each frame even if we
+ // do not send, to make sure that prediction is instant
cl.cmd.time = cl.time;
cl.cmd.sequence = cls.netcon->outgoing_unreliable_sequence;
cl.cmd.msec = 100;
cl.cmd.frametime = cl.cmd.msec * (1.0 / 1000.0);
- cl.cmd.predicted = cl_movement.integer;
+ cl.cmd.predicted = cl_movement.integer != 0;
// movement is set by input code (forwardmove/sidemove/upmove)
// always dump the first two moves, because they may contain leftover inputs from the last level
break;
case PROTOCOL_DARKPLACES6:
case PROTOCOL_DARKPLACES7:
- // FIXME: cl.movecmd[0].buttons & 16 is +button5, Nexuiz specific
+ // FIXME: cl.cmd.buttons & 16 is +button5, Nexuiz specific
cl.cmd.crouch = (cl.cmd.buttons & 16) != 0;
break;
case PROTOCOL_UNKNOWN:
break;
}
- cl.movecmd[0] = cl.cmd;
+ if (quemove)
+ cl.movecmd[0] = cl.cmd;
// don't predict more than 200fps
if (realtime >= cl.lastpackettime + 0.005)
// don't send too often or else network connections can get clogged by a
// high renderer framerate
packettime = 1.0 / bound(1, cl_netfps.value, 1000);
- // send input every frame in singleplayer
- if (cl.islocalgame)
- packettime = 0;
- // always send if buttons changed or an impulse is pending
- // even if it violates the rate limit!
- if (!cl.movecmd[0].impulse && (!cl_netimmediatebuttons.integer || cl.movecmd[0].buttons == cl.movecmd[1].buttons))
+ if (cl.movevars_timescale && cl.movevars_ticrate)
{
- // don't choke the connection with packets (obey rate limit)
- if ((cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS) && !NetConn_CanSend(cls.netcon) && !cl.islocalgame)
- return;
- // don't send too often (cl_netfps)
- if (realtime < cl.lastpackettime + packettime)
- return;
+ float maxtic = cl.movevars_ticrate / cl.movevars_timescale;
+ packettime = min(packettime, maxtic);
}
+
+ // do not send 0ms packets because they mess up physics
+ if(cl.cmd.msec == 0 && cl.time > cl.oldtime && (cls.protocol == PROTOCOL_QUAKEWORLD || cls.signon == SIGNONS))
+ return;
+ // always send if buttons changed or an impulse is pending
+ // even if it violates the rate limit!
+ important = (cl.cmd.impulse || (cl_netimmediatebuttons.integer && cl.cmd.buttons != cl.movecmd[1].buttons));
+ // don't send too often (cl_netfps)
+ if (!important && realtime < cl.lastpackettime + packettime)
+ return;
+ // don't choke the connection with packets (obey rate limit)
+ // it is important that this check be last, because it adds a new
+ // frame to the shownetgraph output and any cancelation after this
+ // will produce a nasty spike-like look to the netgraph
+ // we also still send if it is important
+ if (!NetConn_CanSend(cls.netcon) && !important)
+ return;
// try to round off the lastpackettime to a multiple of the packet interval
// (this causes it to emit packets at a steady beat)
if (packettime > 0)
MSG_WriteByte(&buf, 0);
// packet loss percentage
for (j = 0, packetloss = 0;j < NETGRAPH_PACKETS;j++)
- if (cls.netcon->incoming_unreliablesize[j] == NETGRAPH_LOSTPACKET)
+ if (cls.netcon->incoming_netgraph[j].unreliablebytes == NETGRAPH_LOSTPACKET)
packetloss++;
packetloss = packetloss * 100 / NETGRAPH_PACKETS;
MSG_WriteByte(&buf, packetloss);
// write most recent 3 moves
QW_MSG_WriteDeltaUsercmd(&buf, &nullcmd, &cl.movecmd[2]);
QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[2], &cl.movecmd[1]);
- QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[1], &cl.movecmd[0]);
+ QW_MSG_WriteDeltaUsercmd(&buf, &cl.movecmd[1], &cl.cmd);
// calculate the checksum
buf.data[checksumindex] = COM_BlockSequenceCRCByteQW(buf.data + checksumindex + 1, buf.cursize - checksumindex - 1, cls.netcon->outgoing_unreliable_sequence);
// if delta compression history overflows, request no delta
case PROTOCOL_NEHAHRABJP3:
// 5 bytes
MSG_WriteByte (&buf, clc_move);
- MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
+ MSG_WriteFloat (&buf, cl.cmd.time); // last server packet time
// 3 bytes
for (i = 0;i < 3;i++)
- MSG_WriteAngle8i (&buf, cl.movecmd[0].viewangles[i]);
+ MSG_WriteAngle8i (&buf, cl.cmd.viewangles[i]);
// 6 bytes
- MSG_WriteCoord16i (&buf, cl.movecmd[0].forwardmove);
- MSG_WriteCoord16i (&buf, cl.movecmd[0].sidemove);
- MSG_WriteCoord16i (&buf, cl.movecmd[0].upmove);
+ MSG_WriteCoord16i (&buf, cl.cmd.forwardmove);
+ MSG_WriteCoord16i (&buf, cl.cmd.sidemove);
+ MSG_WriteCoord16i (&buf, cl.cmd.upmove);
// 2 bytes
- MSG_WriteByte (&buf, cl.movecmd[0].buttons);
- MSG_WriteByte (&buf, cl.movecmd[0].impulse);
+ MSG_WriteByte (&buf, cl.cmd.buttons);
+ MSG_WriteByte (&buf, cl.cmd.impulse);
break;
case PROTOCOL_DARKPLACES2:
case PROTOCOL_DARKPLACES3:
// 5 bytes
MSG_WriteByte (&buf, clc_move);
- MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
+ MSG_WriteFloat (&buf, cl.cmd.time); // last server packet time
// 12 bytes
for (i = 0;i < 3;i++)
- MSG_WriteAngle32f (&buf, cl.movecmd[0].viewangles[i]);
+ MSG_WriteAngle32f (&buf, cl.cmd.viewangles[i]);
// 6 bytes
- MSG_WriteCoord16i (&buf, cl.movecmd[0].forwardmove);
- MSG_WriteCoord16i (&buf, cl.movecmd[0].sidemove);
- MSG_WriteCoord16i (&buf, cl.movecmd[0].upmove);
+ MSG_WriteCoord16i (&buf, cl.cmd.forwardmove);
+ MSG_WriteCoord16i (&buf, cl.cmd.sidemove);
+ MSG_WriteCoord16i (&buf, cl.cmd.upmove);
// 2 bytes
- MSG_WriteByte (&buf, cl.movecmd[0].buttons);
- MSG_WriteByte (&buf, cl.movecmd[0].impulse);
+ MSG_WriteByte (&buf, cl.cmd.buttons);
+ MSG_WriteByte (&buf, cl.cmd.impulse);
break;
case PROTOCOL_DARKPLACES1:
case PROTOCOL_DARKPLACES4:
case PROTOCOL_DARKPLACES5:
// 5 bytes
MSG_WriteByte (&buf, clc_move);
- MSG_WriteFloat (&buf, cl.movecmd[0].time); // last server packet time
+ MSG_WriteFloat (&buf, cl.cmd.time); // last server packet time
// 6 bytes
for (i = 0;i < 3;i++)
- MSG_WriteAngle16i (&buf, cl.movecmd[0].viewangles[i]);
+ MSG_WriteAngle16i (&buf, cl.cmd.viewangles[i]);
// 6 bytes
- MSG_WriteCoord16i (&buf, cl.movecmd[0].forwardmove);
- MSG_WriteCoord16i (&buf, cl.movecmd[0].sidemove);
- MSG_WriteCoord16i (&buf, cl.movecmd[0].upmove);
+ MSG_WriteCoord16i (&buf, cl.cmd.forwardmove);
+ MSG_WriteCoord16i (&buf, cl.cmd.sidemove);
+ MSG_WriteCoord16i (&buf, cl.cmd.upmove);
// 2 bytes
- MSG_WriteByte (&buf, cl.movecmd[0].buttons);
- MSG_WriteByte (&buf, cl.movecmd[0].impulse);
+ MSG_WriteByte (&buf, cl.cmd.buttons);
+ MSG_WriteByte (&buf, cl.cmd.impulse);
case PROTOCOL_DARKPLACES6:
case PROTOCOL_DARKPLACES7:
// set the maxusercmds variable to limit how many should be sent
- maxusercmds = bound(1, cl_netrepeatinput.integer + 1, CL_MAX_USERCMDS);
+ maxusercmds = bound(1, cl_netrepeatinput.integer + 1, min(3, CL_MAX_USERCMDS));
// when movement prediction is off, there's not much point in repeating old input as it will just be ignored
if (!cl.cmd.predicted)
maxusercmds = 1;
if (cls.protocol != PROTOCOL_QUAKEWORLD && buf.cursize)
{
- // ack the last few frame numbers
+ // ack entity frame numbers received since the last input was sent
// (redundent to improve handling of client->server packet loss)
- // for LATESTFRAMENUMS == 3 case this is 15 bytes
+ // if cl_netrepeatinput is 1 and client framerate matches server
+ // framerate, this is 10 bytes, if client framerate is lower this
+ // will be more...
+ int i, j;
+ int oldsequence = cl.cmd.sequence - bound(1, cl_netrepeatinput.integer + 1, 3);
+ if (oldsequence < 1)
+ oldsequence = 1;
for (i = 0;i < LATESTFRAMENUMS;i++)
{
- if (cl.latestframenums[i] > 0)
+ j = (cl.latestframenumsposition + i) % LATESTFRAMENUMS;
+ if (cl.latestsendnums[j] >= oldsequence)
{
if (developer_networkentities.integer >= 10)
- Con_Printf("send clc_ackframe %i\n", cl.latestframenums[i]);
+ Con_Printf("send clc_ackframe %i\n", cl.latestframenums[j]);
MSG_WriteByte(&buf, clc_ackframe);
- MSG_WriteLong(&buf, cl.latestframenums[i]);
+ MSG_WriteLong(&buf, cl.latestframenums[j]);
}
}
}
if (buf.cursize || cls.netcon->message.cursize)
NetConn_SendUnreliableMessage(cls.netcon, &buf, cls.protocol, max(20*(buf.cursize+40), cl_rate.integer), false);
- // update the cl.movecmd array which holds the most recent moves,
- // because we now need a new slot for the next input
- for (i = CL_MAX_USERCMDS - 1;i >= 1;i--)
- cl.movecmd[i] = cl.movecmd[i-1];
- cl.movecmd[0].msec = 0;
- cl.movecmd[0].frametime = 0;
+ if (quemove)
+ {
+ // update the cl.movecmd array which holds the most recent moves,
+ // because we now need a new slot for the next input
+ for (i = CL_MAX_USERCMDS - 1;i >= 1;i--)
+ cl.movecmd[i] = cl.movecmd[i-1];
+ cl.movecmd[0].msec = 0;
+ cl.movecmd[0].frametime = 0;
+ }
// clear button 'click' states
in_attack.state &= ~2;
// LordHavoc: added bestweapon command
Cmd_AddCommand ("bestweapon", IN_BestWeapon, "send an impulse number to server to select the first usable weapon out of several (example: 8 7 6 5 4 3 2 1)");
+#if 0
Cmd_AddCommand ("cycleweapon", IN_CycleWeapon, "send an impulse number to server to select the next usable weapon out of several (example: 9 4 8) if you are holding one of these, and choose the first one if you are holding none of these");
+#endif
Cmd_AddCommand ("register_bestweapon", IN_BestWeapon_Register_f, "(for QC usage only) change weapon parameters to be used by bestweapon; stuffcmd this in ClientConnect");
+ Cvar_RegisterVariable(&cl_movecliptokeyboard);
Cvar_RegisterVariable(&cl_movement);
+ Cvar_RegisterVariable(&cl_movement_nettimeout);
Cvar_RegisterVariable(&cl_movement_minping);
Cvar_RegisterVariable(&cl_movement_track_canjump);
Cvar_RegisterVariable(&cl_movement_maxspeed);