- Con_DPrintf("client move->time %f > sv.time %f, kicking\n", (float)move->time, (float)sv.time);
- // if the client is lying about time, we have definitively detected a
- // speed cheat attempt of the worst sort, and we can immediately kick
- // the offending player off.
- // this fixes the timestamp to prevent a speed cheat from working
- move->time = sv.time;
- // but we kick the player for good measure
- kickplayer = true;
+ // process the moves in order and ignore old ones
+ // but always trust the latest move
+ // (this deals with bogus initial move sequences after level change,
+ // where the client will eventually catch up with the level change
+ // and reset its move sequence)
+ for (moveindex = 0;moveindex < sv_numreadmoves;moveindex++)
+ {
+ usercmd_t *move = sv_readmoves + moveindex;
+ if (host_client->movesequence < move->sequence || moveindex == sv_numreadmoves - 1)
+ {
+#if DEBUGMOVES
+ Con_Printf("%smove #%i %ims (%ims) %i %i '%i %i %i' '%i %i %i'\n", (move->time - host_client->cmd.time) > sv.frametime ? "^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
+ moveframetime = bound(0, move->time - host_client->cmd.time, 0.1);
+ //Con_Printf("movesequence = %i (%i lost), moveframetime = %f\n", move->sequence, move->sequence ? move->sequence - host_client->movesequence - 1 : 0, moveframetime);
+ host_client->cmd = *move;
+ host_client->movesequence = move->sequence;
+
+ // if using prediction, we need to perform moves when packets are
+ // received, even if multiple occur in one frame
+ // (they can't go beyond the current time so there is no cheat issue
+ // 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_waitforinput cvar)
+ if (moveframetime <= 0)
+ continue;
+ oldframetime = prog->globals.server->frametime;
+ oldframetime2 = sv.frametime;
+ // update ping time for qc to see while executing this move
+ host_client->ping = host_client->cmd.receivetime - host_client->cmd.time;
+ // the server and qc frametime values must be changed temporarily
+ prog->globals.server->frametime = sv.frametime = moveframetime;
+ // if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction)
+ if (sv.frametime > 0.05)
+ {
+ prog->globals.server->frametime = sv.frametime = moveframetime * 0.5f;
+ SV_Physics_ClientMove();
+ }
+ SV_Physics_ClientMove();
+ sv.frametime = oldframetime2;
+ prog->globals.server->frametime = oldframetime;
+ host_client->clmovement_skipphysicsframes = sv_clmovement_waitforinput.integer;
+ }
+ }