// sv_user.c -- server code for moving users
#include "quakedef.h"
+#include "sv_demo.h"
#define DEBUGMOVES 0
static usercmd_t cmd;
+extern cvar_t sv_autodemo_perclient;
/*
===============
// noclip
VectorCopy (wishvel, host_client->edict->fields.server->velocity);
}
- else if (onground && (!sv_gameplayfix_qwplayerphysics.integer || !host_client->edict->fields.server->button2 || !((int)host_client->edict->fields.server->flags & FL_JUMPRELEASED)))
+ else if (onground)
{
SV_UserFriction ();
SV_Accelerate ();
// 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 ();
- move->time = MSG_ReadFloat ();
+ move->time = move->clienttime = MSG_ReadFloat ();
if (msg_badread) Con_Printf("SV_ReadClientMessage: badread at %s:%i\n", __FILE__, __LINE__);
move->receivetime = (float)sv.time;
if (ceil(max(sv_readmoves[sv_numreadmoves-1].receivetime - sv_readmoves[sv_numreadmoves-1].time, 0) * 1000.0) < sv_clmovement_minping.integer)
host_client->clmovement_disabletimeout = 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_waitforinput.integer > 0 && host_client->clmovement_disabletimeout <= realtime && host_client->edict->fields.server->movetype == MOVETYPE_WALK && (!(val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.disableclientprediction)) || !val->_float))
+ if (sv_readmoves[sv_numreadmoves-1].sequence && sv_clmovement_enable.integer && sv_clmovement_inputtimeout.value > 0 && host_client->clmovement_disabletimeout <= realtime && host_client->edict->fields.server->movetype == MOVETYPE_WALK && (!(val = PRVM_EDICTFIELDVALUE(host_client->edict, prog->fieldoffsets.disableclientprediction)) || !val->_float))
{
// process the moves in order and ignore old ones
// but always trust the latest move
Con_Printf("%smove #%i %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, 0.1);
+ moveframetime = bound(0, move->time - host_client->cmd.time, min(0.1, sv_clmovement_inputtimeout.value));
+
+ // discard (treat like lost) moves with too low distance from
+ // the previous one to prevent hacks using float inaccuracy
+ // clients will see this as packet loss in the netgraph
+ if(moveframetime < 0.0005)
+ continue;
+
//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;
// (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)
+ // determined by the sv_clmovement_inputtimeout cvar)
if (moveframetime <= 0)
continue;
oldframetime = prog->globals.server->frametime;
SV_Physics_ClientMove();
sv.frametime = oldframetime2;
prog->globals.server->frametime = oldframetime;
- host_client->clmovement_skipphysicsframes = sv_clmovement_waitforinput.integer;
+ host_client->clmovement_inputtimeout = sv_clmovement_inputtimeout.value;
}
}
}
}
}
// now copy the new move
- sv_readmoves[sv_numreadmoves-1].time = max(sv_readmoves[sv_numreadmoves-1].time, host_client->cmd.time); // prevent backstepping of time
host_client->cmd = sv_readmoves[sv_numreadmoves-1];
+ host_client->cmd.time = max(host_client->cmd.time, sv.time);
+ // physics will run up to sv.time, so allow no predicted moves
+ // before that otherwise, there is a speedhack by turning
+ // prediction on and off repeatedly on client side because the
+ // engine would run BOTH client and server physics for the same
+ // time
host_client->movesequence = 0;
// make sure that normal physics takes over immediately
- host_client->clmovement_skipphysicsframes = 0;
+ host_client->clmovement_inputtimeout = 0;
}
// calculate average ping time
- host_client->ping = host_client->cmd.receivetime - host_client->cmd.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.time;
+ 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];
void SV_FrameLost(int framenum)
{
if (host_client->entitydatabase5)
+ {
EntityFrame5_LostFrame(host_client->entitydatabase5, framenum);
+ EntityFrameCSQC_LostFrame(host_client, framenum);
+ }
}
void SV_FrameAck(int framenum)
int cmd, num, start;
char *s, *p, *q;
+ if(sv_autodemo_perclient.integer >= 2)
+ SV_WriteDemoMessage(host_client, &(host_client->netconnection->message), true);
+
//MSG_BeginReading ();
sv_numreadmoves = 0;
int crc;
unsigned char *temp;
FS_Seek(host_client->download_file, 0, SEEK_SET);
- temp = Mem_Alloc(tempmempool, size);
+ temp = (unsigned char *) Mem_Alloc(tempmempool, size);
FS_Read(host_client->download_file, temp, size);
crc = CRC_Block(temp, size);
Mem_Free(temp);
// calculated crc, send the file info to the client
// (so that it can verify the data)
- Host_ClientCommands(va("\ncl_downloadfinished %i %i %s\n", size, crc, host_client->download_name));
+ Host_ClientCommands("\ncl_downloadfinished %i %i %s\n", size, crc, host_client->download_name);
Con_DPrintf("Download of %s by %s has finished\n", host_client->download_name, host_client->name);
FS_Close(host_client->download_file);
host_client->download_file = NULL;