#include "sv_demo.h"
#define DEBUGMOVES 0
-static usercmd_t cmd;
+static usercmd_t usercmd;
extern cvar_t sv_autodemo_perclient;
+extern cvar_t sv_rollangle;
+extern cvar_t sv_rollspeed;
/*
==================
PRVM_serverglobaledict(self) = PRVM_EDICT_TO_PROG(host_client->edict);
prog->ExecuteProgram(prog, PRVM_serverfunction(ClientConnect), "QC function ClientConnect is missing");
- if (cls.state == ca_dedicated)
- Con_Printf("%s connected\n", host_client->name);
+ Con_Printf("%s connected\n", host_client->name);
PRVM_serverglobalfloat(time) = sv.time;
prog->ExecuteProgram(prog, PRVM_serverfunction(PutClientInServer), "QC function PutClientInServer is missing");
static vec3_t wishdir, forward, right, up;
static float wishspeed;
-static qboolean onground;
+static qbool onground;
/*
==================
AngleVectors(v_angle, forward, right, up);
for (i=0 ; i<3 ; i++)
- wishvel[i] = forward[i]*cmd.forwardmove + right[i]*cmd.sidemove;
+ wishvel[i] = forward[i]*usercmd.forwardmove + right[i]*usercmd.sidemove;
- if (!cmd.forwardmove && !cmd.sidemove && !cmd.upmove)
+ if (!usercmd.forwardmove && !usercmd.sidemove && !usercmd.upmove)
wishvel[2] -= 60; // drift towards bottom
else
- wishvel[2] += cmd.upmove;
+ wishvel[2] += usercmd.upmove;
fwishspeed = VectorLength(wishvel);
if (fwishspeed > sv_maxspeed.value)
wishvel[1] = PRVM_serveredictvector(host_client->edict, angles)[1];
AngleVectors (wishvel, forward, right, up);
- fmove = cmd.forwardmove;
- smove = cmd.sidemove;
+ fmove = usercmd.forwardmove;
+ smove = usercmd.sidemove;
// hack to not let you back into teleporter
if (sv.time < PRVM_serveredictfloat(host_client->edict, teleport_time) && fmove < 0)
wishvel[i] = forward[i]*fmove + right[i]*smove;
if ((int)PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_WALK)
- wishvel[2] += cmd.upmove;
+ wishvel[2] += usercmd.upmove;
VectorCopy (wishvel, wishdir);
wishspeed = VectorNormalizeLength(wishdir);
/*
===================
-SV_ClientThink
+SV_PlayerPhysics
the move fields specify an intended velocity in pix/sec
the angle fields specify an exact angular motion in degrees
===================
*/
-void SV_ClientThink (void)
+void SV_PlayerPhysics (void)
{
prvm_prog_t *prog = SVVM_prog;
vec3_t v_angle, angles, velocity;
// make sure the velocity is sane (not a NaN)
SV_CheckVelocity(host_client->edict);
- // LadyHavoc: QuakeC replacement for SV_ClientThink (player movement)
+ // LadyHavoc: QuakeC replacement for SV_PlayerPhysics (player movement)
if (PRVM_serverfunction(SV_PlayerPhysics) && sv_playerphysicsqc.integer)
{
PRVM_serverglobalfloat(time) = sv.time;
if (PRVM_serveredictfloat(host_client->edict, health) <= 0)
return;
- cmd = host_client->cmd;
+ usercmd = host_client->cmd;
// angles
// show 1/3 the pitch angle and all the roll angle
VectorAdd (PRVM_serveredictvector(host_client->edict, v_angle), PRVM_serveredictvector(host_client->edict, punchangle), v_angle);
VectorCopy(PRVM_serveredictvector(host_client->edict, angles), angles);
VectorCopy(PRVM_serveredictvector(host_client->edict, velocity), velocity);
- PRVM_serveredictvector(host_client->edict, angles)[ROLL] = V_CalcRoll (angles, velocity)*4;
+ PRVM_serveredictvector(host_client->edict, angles)[ROLL] = Com_CalcRoll (angles, velocity, sv_rollangle.value, sv_rollspeed.value)*4;
if (!PRVM_serveredictfloat(host_client->edict, fixangle))
{
PRVM_serveredictvector(host_client->edict, angles)[PITCH] = -v_angle[PITCH]/3;
return;
}
- /*
- // Player is (somehow) outside of the map, or flying, or noclipping
- if (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP && (PRVM_serveredictfloat(host_client->edict, movetype) == MOVETYPE_FLY || SV_TestEntityPosition (host_client->edict)))
- //if (PRVM_serveredictfloat(host_client->edict, movetype) == MOVETYPE_NOCLIP || PRVM_serveredictfloat(host_client->edict, movetype) == MOVETYPE_FLY || SV_TestEntityPosition (host_client->edict))
- {
- SV_FreeMove ();
- return;
- }
- */
-
// walk
if ((PRVM_serveredictfloat(host_client->edict, waterlevel) >= 2) && (PRVM_serveredictfloat(host_client->edict, movetype) != MOVETYPE_NOCLIP))
{
// read ping time
if (sv.protocol != PROTOCOL_QUAKE && sv.protocol != PROTOCOL_QUAKEDP && sv.protocol != PROTOCOL_NEHAHRAMOVIE && sv.protocol != PROTOCOL_NEHAHRABJP && sv.protocol != PROTOCOL_NEHAHRABJP2 && sv.protocol != PROTOCOL_NEHAHRABJP3 && sv.protocol != PROTOCOL_DARKPLACES1 && sv.protocol != PROTOCOL_DARKPLACES2 && sv.protocol != PROTOCOL_DARKPLACES3 && sv.protocol != PROTOCOL_DARKPLACES4 && sv.protocol != PROTOCOL_DARKPLACES5 && sv.protocol != PROTOCOL_DARKPLACES6)
move->sequence = MSG_ReadLong(&sv_message);
- move->time = move->clienttime = MSG_ReadFloat(&sv_message);
+ move->time = MSG_ReadFloat(&sv_message);
if (sv_message.badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__);
move->receivetime = (float)sv.time;
#endif
// limit reported time to current time
// (incase the client is trying to cheat)
- move->time = min(move->time, move->receivetime + sv.frametime);
+ move->time = min(move->time, sv.time + sv.frametime);
// read current angles
for (i = 0;i < 3;i++)
sv_readmoves[sv_numreadmoves++] = *move;
// movement packet loss tracking
- if(move->sequence)
+ // bones_was_here: checking begun prevents heavy loss detection right after a map change
+ if(move->sequence && host_client->begun)
{
if(move->sequence > host_client->movement_highestsequence_seen)
{
{
prvm_prog_t *prog = SVVM_prog;
int moveindex;
- float moveframetime;
+ double moveframetime;
double oldframetime;
double oldframetime2;
-#ifdef NUM_PING_TIMES
- double total;
-#endif
+
if (sv_numreadmoves < 1)
return;
// only start accepting input once the player is spawned
#if DEBUGMOVES
Con_Printf("SV_ExecuteClientMoves: read %i moves at sv.time %f\n", sv_numreadmoves, (float)sv.time);
#endif
+
+ // update ping time
+ host_client->ping = max(sv_readmoves[sv_numreadmoves-1].receivetime - sv_readmoves[sv_numreadmoves-1].time, 0);
+
// disable clientside movement prediction in some cases
- if (ceil(max(sv_readmoves[sv_numreadmoves-1].receivetime - sv_readmoves[sv_numreadmoves-1].time, 0) * 1000.0) < sv_clmovement_minping.integer)
+ if (host_client->ping * 1000.0 < sv_clmovement_minping.value || host_client->ping * 1000.0 > sv_clmovement_maxping.value)
host_client->clmovement_disabletimeout = host.realtime + sv_clmovement_minping_disabletime.value / 1000.0;
+
// several conditions govern whether clientside movement prediction is allowed
if (sv_readmoves[sv_numreadmoves-1].sequence && sv_clmovement_enable.integer && sv_clmovement_inputtimeout.value > 0 && host_client->clmovement_disabletimeout <= host.realtime && (PRVM_serveredictfloat(host_client->edict, disableclientprediction) == -1 || (PRVM_serveredictfloat(host_client->edict, movetype) == MOVETYPE_WALK && (!PRVM_serveredictfloat(host_client->edict, disableclientprediction)))))
{
Con_Printf("%smove #%u %ims (%ims) %i %i '%i %i %i' '%i %i %i'\n", (move->time - host_client->cmd.time) > sv.frametime * 1.01 ? "^1" : "^2", move->sequence, (int)floor((move->time - host_client->cmd.time) * 1000.0 + 0.5), (int)floor(move->time * 1000.0 + 0.5), move->impulse, move->buttons, (int)move->viewangles[0], (int)move->viewangles[1], (int)move->viewangles[2], (int)move->forwardmove, (int)move->sidemove, (int)move->upmove);
#endif
// this is a new move
- move->time = bound(sv.time - 1, move->time, sv.time); // prevent slowhack/speedhack combos
- move->time = max(move->time, host_client->cmd.time); // prevent backstepping of time
- moveframetime = bound(0, move->time - host_client->cmd.time, min(0.1, sv_clmovement_inputtimeout.value));
+ // prevent backstepping of time and slowhack/speedhack combos
+ move->time = bound(host_client->cmd.time, move->time, sv.time);
+ // bones_was_here: limit moveframetime to a multiple of sv.frametime to match inputtimeout behaviour
+ moveframetime = min(move->time - host_client->cmd.time, min(0.1, sys_ticrate.value > 0.0 && sv.frametime > 0.0 ? sv.frametime * ceil(sv_clmovement_inputtimeout.value / sv.frametime) : sv_clmovement_inputtimeout.value));
// discard (treat like lost) moves with too low distance from
// the previous one to prevent hacks using float inaccuracy
// count the move as LOST if we don't
// execute it but it has higher
// sequence count
- if(host_client->movesequence)
- if(move->sequence > host_client->movesequence)
- host_client->movement_count[(move->sequence) % NETGRAPH_PACKETS] = -1;
+ if(host_client->movesequence && move->sequence > host_client->movesequence)
+ {
+ host_client->movement_count[(move->sequence) % NETGRAPH_PACKETS] = -1;
+
+ // bones_was_here: if we are discarding the move due to inputtimeout,
+ // and it's the last received and has the highest sequence count,
+ // set cmd.time to allow for persistent latency increases,
+ // and to calculate the moveframetime the client expects next frame
+ if(host_client->clmovement_inputtimeout == -666 && moveindex == sv_numreadmoves - 1 && sv_clmovement_inputtimeout_strict.integer < 2)
+ host_client->cmd.time = move->time - sv.frametime;
+ }
+
continue;
}
// with this approach, and if they don't send input for a while they
// start moving anyway, so the longest 'lagaport' possible is
// determined by the sv_clmovement_inputtimeout cvar)
- if (moveframetime <= 0)
- continue;
oldframetime = PRVM_serverglobalfloat(frametime);
oldframetime2 = sv.frametime;
// update ping time for qc to see while executing this move
SV_Physics_ClientMove();
sv.frametime = oldframetime2;
PRVM_serverglobalfloat(frametime) = oldframetime;
- host_client->clmovement_inputtimeout = sv_clmovement_inputtimeout.value;
+ host_client->clmovement_inputtimeout = min(0.1, sv_clmovement_inputtimeout.value);
}
}
}
// make sure that normal physics takes over immediately
host_client->clmovement_inputtimeout = 0;
}
-
- // calculate average ping time
- host_client->ping = host_client->cmd.receivetime - host_client->cmd.clienttime;
-#ifdef NUM_PING_TIMES
- host_client->ping_times[host_client->num_pings % NUM_PING_TIMES] = host_client->cmd.receivetime - host_client->cmd.clienttime;
- host_client->num_pings++;
- for (i=0, total = 0;i < NUM_PING_TIMES;i++)
- total += host_client->ping_times[i];
- host_client->ping = total / NUM_PING_TIMES;
-#endif
}
void SV_ApplyClientMove (void)
PRVM_serveredictfloat(host_client->edict, ping_movementloss) = movementloss / (float) NETGRAPH_PACKETS;
}
-static qboolean SV_FrameLost(int framenum)
+static qbool SV_FrameLost(int framenum)
{
if (host_client->entitydatabase5)
{
if (strncasecmp(s, "spawn", 5) == 0
|| strncasecmp(s, "begin", 5) == 0
|| strncasecmp(s, "prespawn", 8) == 0)
- Cmd_ExecuteString (&cmd_serverfromclient, s, src_client, true);
+ Cmd_ExecuteString (cmd_serverfromclient, s, src_client, true);
else if (PRVM_serverfunction(SV_ParseClientCommand))
{
int restorevm_tempstringsbuf_cursize;
prog->tempstringsbuf.cursize = restorevm_tempstringsbuf_cursize;
}
else
- Cmd_ExecuteString (&cmd_serverfromclient, s, src_client, true);
+ Cmd_ExecuteString (cmd_serverfromclient, s, src_client, true);
break;
clc_stringcmd_invalid: