if (cl.movement_queue[i].sequence > cls.netcon->qw.incoming_sequence)
cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i];
else if (i == 0)
- cl.movement_replay_canjump = !cl.movement_queue[i].jump;
+ cl.movement_replay_canjump = !cl.movement_queue[i].jump; // FIXME: this logic is quite broken
}
// add to input queue if there is room
if (cl.movement_numqueue < (int)(sizeof(cl.movement_queue)/sizeof(cl.movement_queue[0])))
{
int i;
int n;
- double lasttime = cl.movement_numqueue >= 0 ? cl.movement_queue[cl.movement_numqueue - 1].time : 0;
+ double lasttime = (cls.protocol == PROTOCOL_DARKPLACES6 || cls.protocol == PROTOCOL_DARKPLACES7) ? cl.mtime[1] : (cl.movement_numqueue >= 0 ? cl.movement_queue[cl.movement_numqueue - 1].time : 0);
// remove stale queue items
n = cl.movement_numqueue;
cl.movement_numqueue = 0;
if (cl.movement_queue[i].sequence > cl.servermovesequence)
cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i];
else if (i == 0)
- cl.movement_replay_canjump = !cl.movement_queue[i].jump;
+ cl.movement_replay_canjump = !cl.movement_queue[i].jump; // FIXME: this logic is quite broken
}
}
else
if (cl.movement_queue[i].time >= cl.mtime[0] - cl_movement_latency.value / 1000.0 && cl.movement_queue[i].time <= cl.mtime[0])
cl.movement_queue[cl.movement_numqueue++] = cl.movement_queue[i];
else if (i == 0)
- cl.movement_replay_canjump = !cl.movement_queue[i].jump;
+ cl.movement_replay_canjump = !cl.movement_queue[i].jump; // FIXME: this logic is quite broken
}
}
// add to input queue if there is room
s.movevars_stepheight = cl_movement_stepheight.value;
}
- if (cl.movement)
+ cl.movement_predicted = (cl_movement.integer && cls.signon == SIGNONS && cl.stats[STAT_HEALTH] > 0 && !cl.intermission) && ((cls.protocol != PROTOCOL_DARKPLACES6 && cls.protocol != PROTOCOL_DARKPLACES7) || cl.servermovesequence);
+ if (cl.movement_predicted)
{
//Con_Printf("%f: ", cl.mtime[0]);
accumtotal++;
#endif
- cl.movement = cl_movement.integer && cls.signon == SIGNONS && cl.stats[STAT_HEALTH] > 0 && !cl.intermission;
if (cl_movement.integer && cls.signon == SIGNONS && cls.protocol != PROTOCOL_QUAKEWORLD)
{
if (!cl.movement_needupdate)
// movement lerp
// if it's the player entity, update according to client movement
- if (e == cl.entities + cl.playerentity && cl.movement)// && !e->csqc)
+ if (e == cl.entities + cl.playerentity && cl.movement_predicted)// && !e->csqc)
{
lerp = (cl.time - cl.movement_time[1]) / (cl.movement_time[0] - cl.movement_time[1]);
lerp = bound(0, lerp, 1);
// client movement simulation
// these fields are only updated by CL_ClientMovement (called by CL_SendMove after parsing each network packet)
- qboolean movement;
+ // set by CL_ClientMovement_Replay functions
+ qboolean movement_predicted;
// this is set true by svc_time parsing and causes a new movement to be
// queued for prediction purposes
qboolean movement_needupdate;
vec3_t movement_velocity;
// queue of proposed moves
int movement_numqueue;
- client_movementqueue_t movement_queue[64];
+ client_movementqueue_t movement_queue[256];
int movesequence;
int servermovesequence;
// whether the replay should allow a jump at the first sequence
float ping;
// this is used by sv_clmovement_minping code
- double clmovement_disable_minpingtimeout;
+ double clmovement_disabletimeout;
+ // this is used by sv_clmvoement_waitforinput code
+ int clmovement_skipphysicsframes;
// spawn parms are carried from level to level
float spawn_parms[NUM_SPAWN_PARMS];
extern cvar_t sv_clmovement_enable;
extern cvar_t sv_clmovement_minping;
extern cvar_t sv_clmovement_minping_disabletime;
+extern cvar_t sv_clmovement_waitforinput;
server_t sv;
server_static_t svs;
Cvar_RegisterVariable (&sv_clmovement_enable);
Cvar_RegisterVariable (&sv_clmovement_minping);
Cvar_RegisterVariable (&sv_clmovement_minping_disabletime);
+ Cvar_RegisterVariable (&sv_clmovement_waitforinput);
Cvar_RegisterVariable (&sv_idealpitchscale);
Cvar_RegisterVariable (&sv_aim);
Cvar_RegisterVariable (&sv_nostep);
cvar_t sv_sound_watersplash = {0, "sv_sound_watersplash", "misc/h2ohit1.wav", "sound to play when MOVETYPE_FLY/TOSS/BOUNCE/STEP entity enters or leaves water (empty cvar disables the sound)"};
cvar_t sv_sound_land = {0, "sv_sound_land", "demon/dland2.wav", "sound to play when MOVETYPE_STEP entity hits the ground at high speed (empty cvar disables the sound)"};
+// TODO: move this extern to server.h
+extern cvar_t sv_clmovement_waitforinput;
+
#define MOVE_EPSILON 0.01
void SV_Physics_Toss (prvm_edict_t *ent);
if (!host_client->spawned)
memset(&host_client->cmd, 0, sizeof(host_client->cmd));
// don't run physics here if running asynchronously
- else if (!host_client->movesequence)
+ else if (host_client->clmovement_skipphysicsframes > 0)
+ host_client->clmovement_skipphysicsframes--;
+ else
SV_Physics_ClientEntity(ent);
}
}
cvar_t sv_clmovement_enable = {0, "sv_clmovement_enable", "1", "whether to allow clients to use cl_movement prediction, which can cause choppy movement on the server which may annoy other players"};
cvar_t sv_clmovement_minping = {0, "sv_clmovement_minping", "100", "if client ping is below this time in milliseconds, then their ability to use cl_movement prediction is disabled for a while (as they don't need it)"};
cvar_t sv_clmovement_minping_disabletime = {0, "sv_clmovement_minping_disabletime", "1000", "when client falls below minping, disable their prediction for this many milliseconds (should be at least 1000 or else their prediction may turn on/off frequently)"};
+cvar_t sv_clmovement_waitforinput = {0, "sv_clmovement_waitforinput", "2", "when a client does not send input for this many frames, force them to move anyway (unlike QuakeWorld)"};
static usercmd_t cmd;
// disable clientside movement prediction in some cases
if (ceil((move->receivetime - move->time) * 1000.0) < sv_clmovement_minping.integer)
- host_client->clmovement_disable_minpingtimeout = realtime + sv_clmovement_minping_disabletime.value / 1000.0;
- if (!sv_clmovement_enable.integer || host_client->clmovement_disable_minpingtimeout > realtime)
+ host_client->clmovement_disabletimeout = realtime + sv_clmovement_minping_disabletime.value / 1000.0;
+ if (!sv_clmovement_enable.integer || host_client->clmovement_disabletimeout > realtime)
move->sequence = 0;
if (!host_client->spawned)
{
// apply the latest accepted move to the entity fields
host_client->movesequence = move->sequence;
- if (host_client->movesequence)
+ if (host_client->movesequence && sv_clmovement_waitforinput.integer > 0)
{
double frametime = bound(0, move->time - oldmovetime, 0.1);
double oldframetime = prog->globals.server->frametime;
prog->globals.server->frametime = frametime;
SV_Physics_ClientEntity(host_client->edict);
prog->globals.server->frametime = oldframetime;
+ host_client->clmovement_skipphysicsframes = sv_clmovement_waitforinput.integer;
}
}
return kickplayer;