Put lag compensation clamping in a separate method, under a cvar that is disabled for now

There have been reports of hit registration issues, this is most likely caused by the clamping system to mitigate an exploit. Put behind a convar and disabled for now until debugged.
This commit is contained in:
Kawe Mazidjatari 2023-10-25 23:31:02 +02:00
parent fa76747baa
commit 6bc15b50b5
4 changed files with 69 additions and 51 deletions

View File

@ -121,6 +121,7 @@ ConVar* sv_forceChatToTeamOnly = nullptr;
ConVar* sv_single_core_dedi = nullptr; ConVar* sv_single_core_dedi = nullptr;
ConVar* sv_maxunlag = nullptr; ConVar* sv_maxunlag = nullptr;
ConVar* sv_unlag_clamp = nullptr;
ConVar* sv_clockcorrection_msecs = nullptr; ConVar* sv_clockcorrection_msecs = nullptr;
ConVar* sv_updaterate_sp = nullptr; ConVar* sv_updaterate_sp = nullptr;
@ -364,6 +365,9 @@ void ConVar_StaticInit(void)
#endif // !DEDICATED #endif // !DEDICATED
sv_language = ConVar::StaticCreate("sv_language", "english", FCVAR_RELEASE, "Language of the server. Sent to MasterServer for localising error messages.", false, 0.f, false, 0.f, LanguageChanged_f, nullptr); sv_language = ConVar::StaticCreate("sv_language", "english", FCVAR_RELEASE, "Language of the server. Sent to MasterServer for localising error messages.", false, 0.f, false, 0.f, LanguageChanged_f, nullptr);
sv_unlag_clamp = ConVar::StaticCreate("sv_unlag_clamp", "0", FCVAR_RELEASE, "Clamp the difference between the current time and received command time to sv_maxunlag + sv_clockcorrection_msecs.", false, 0.f, false, 0.f, nullptr, nullptr);
sv_showconnecting = ConVar::StaticCreate("sv_showconnecting" , "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr); sv_showconnecting = ConVar::StaticCreate("sv_showconnecting" , "1", FCVAR_RELEASE, "Logs information about the connecting client to the console.", false, 0.f, false, 0.f, nullptr, nullptr);
sv_globalBanlist = ConVar::StaticCreate("sv_globalBanlist" , "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, nullptr, "0 = Disable, 1 = Enable."); sv_globalBanlist = ConVar::StaticCreate("sv_globalBanlist" , "1", FCVAR_RELEASE, "Determines whether or not to use the global banned list.", false, 0.f, false, 0.f, nullptr, "0 = Disable, 1 = Enable.");
sv_pylonVisibility = ConVar::StaticCreate("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", false, 0.f, false, 0.f, nullptr, "0 = Offline, 1 = Hidden, 2 = Public."); sv_pylonVisibility = ConVar::StaticCreate("sv_pylonVisibility", "0", FCVAR_RELEASE, "Determines the visibility to the Pylon master server.", false, 0.f, false, 0.f, nullptr, "0 = Offline, 1 = Hidden, 2 = Public.");

View File

@ -110,6 +110,7 @@ extern ConVar* sv_forceChatToTeamOnly;
extern ConVar* sv_single_core_dedi; extern ConVar* sv_single_core_dedi;
extern ConVar* sv_maxunlag; extern ConVar* sv_maxunlag;
extern ConVar* sv_unlag_clamp;
extern ConVar* sv_clockcorrection_msecs; extern ConVar* sv_clockcorrection_msecs;
extern ConVar* sv_updaterate_sp; extern ConVar* sv_updaterate_sp;

View File

@ -109,6 +109,65 @@ void CPlayer::SetTotalExtraClientCmdTimeAttempted(float flAttemptedTime)
} }
} }
//------------------------------------------------------------------------------
// Purpose: clamps the unlag amount to sv_unlag + clockdrift
// Input : *cmd -
//------------------------------------------------------------------------------
void CPlayer::ClampUnlag(CUserCmd* cmd)
{
const CClient* client = g_pServer->GetClient(GetEdict() - 1);
const CNetChan* chan = client->GetNetChan();
const float clockDriftMsecs = sv_clockcorrection_msecs->GetFloat() / 1000.0f;
const float maxUnlag = sv_maxunlag->GetFloat();
const float latencyAmount = Clamp(chan->GetLatency(FLOW_OUTGOING), 0.0f, maxUnlag);
const float serverTime = (*g_pGlobals)->m_flCurTime;
// Command issue time from client, note that this value can be altered
// from the client, and therefore be used to exploit lag compensation.
const float commandTime = cmd->command_time;
const float lastCommandTime = m_LastCmd.command_time;
const float commandDelta = fabs(commandTime - serverTime);
bool recomputeUnlag = false;
// Check delta first, otherwise player could set commandTime to a fixed
// time and circumvent the system, as commandTime < lastCommandTime or
// commandTime > localCurTime will always fail.
if (commandDelta > maxUnlag)
{
// Too much to unlag, clamp to max !!!
recomputeUnlag = true;
DevWarning(eDLL_T::SERVER, "%s: commandDelta( %f ) > maxUnlag( %f ) !!!\n",
__FUNCTION__, commandDelta, maxUnlag);
}
else if (commandTime < (lastCommandTime - clockDriftMsecs))
{
// Can never be lower than last !!!
recomputeUnlag = true;
DevWarning(eDLL_T::SERVER, "%s: cmd->command_time( %f ) < (m_LastCmd.command_time( %f ) - clockDriftMsecs( %f )) !!!\n",
__FUNCTION__, commandTime, lastCommandTime, clockDriftMsecs);
}
else if (commandTime > (serverTime + clockDriftMsecs))
{
// Too far in the future, clamp to max !!!
recomputeUnlag = true;
DevWarning(eDLL_T::SERVER, "%s: cmd->command_time( %f ) > (g_pGlobals->m_flCurTime( %f ) + clockDriftMsecs( %f )) !!!\n",
__FUNCTION__, commandTime, serverTime, clockDriftMsecs);
}
if (recomputeUnlag)
{
// Clamp it to server time minus latency. Note that it could still
// be lower than previous, hence the clamp on the recomputation.
float newCommandTime = Clamp(serverTime - latencyAmount, lastCommandTime, serverTime);
cmd->command_time = newCommandTime;
DevWarning(eDLL_T::SERVER, "%s: Clamped cmd->command_time( %f ) to %f !!!\n",
__FUNCTION__, commandTime, newCommandTime);
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Purpose: processes user cmd's for this player // Purpose: processes user cmd's for this player
// Input : *cmds - // Input : *cmds -
@ -123,16 +182,8 @@ void CPlayer::ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds,
if (totalCmds <= 0) if (totalCmds <= 0)
return; return;
const CClient* client = g_pServer->GetClient(GetEdict() - 1);
const CNetChan* chan = client->GetNetChan();
CUserCmd* lastCmd = &m_Commands[MAX_QUEUED_COMMANDS_PROCESS]; CUserCmd* lastCmd = &m_Commands[MAX_QUEUED_COMMANDS_PROCESS];
const float clockDriftMsecs = sv_clockcorrection_msecs->GetFloat() / 1000.0f;
const float maxUnlag = sv_maxunlag->GetFloat();
const float latencyAmount = Clamp(chan->GetLatency(FLOW_OUTGOING), 0.0f, maxUnlag);
const float serverTime = (*g_pGlobals)->m_flCurTime;
for (int i = totalCmds - 1; i >= 0; i--) for (int i = totalCmds - 1; i >= 0; i--)
{ {
CUserCmd* cmd = &cmds[i]; CUserCmd* cmd = &cmds[i];
@ -147,49 +198,8 @@ void CPlayer::ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds,
if (lastCommandNumber == MAX_QUEUED_COMMANDS_PROCESS) if (lastCommandNumber == MAX_QUEUED_COMMANDS_PROCESS)
return; return;
// Command issue time from client, note that this value can be altered if (sv_unlag_clamp->GetBool())
// from the client, and therefore be used to exploit lag compensation. ClampUnlag(cmd);
const float commandTime = cmd->command_time;
const float lastCommandTime = m_LastCmd.command_time;
const float commandDelta = fabs(commandTime - serverTime);
bool recomputeUnlag = false;
// Check delta first, otherwise player could set commandTime to a fixed
// time and circumvent the system, as commandTime < lastCommandTime or
// commandTime > localCurTime will always fail.
if (commandDelta > maxUnlag)
{
// Too much to unlag, clamp to max !!!
recomputeUnlag = true;
DevWarning(eDLL_T::SERVER, "%s: commandDelta( %f ) > maxUnlag( %f ) !!!\n",
__FUNCTION__, commandDelta, maxUnlag);
}
else if (commandTime < (lastCommandTime - clockDriftMsecs))
{
// Can never be lower than last !!!
recomputeUnlag = true;
DevWarning(eDLL_T::SERVER, "%s: cmd->command_time( %f ) < (m_LastCmd.command_time( %f ) - clockDriftMsecs( %f )) !!!\n",
__FUNCTION__, commandTime, lastCommandTime, clockDriftMsecs);
}
else if (commandTime > (serverTime + clockDriftMsecs))
{
// Too far in the future, clamp to max !!!
recomputeUnlag = true;
DevWarning(eDLL_T::SERVER, "%s: cmd->command_time( %f ) > (g_pGlobals->m_flCurTime( %f ) + clockDriftMsecs( %f )) !!!\n",
__FUNCTION__, commandTime, serverTime, clockDriftMsecs);
}
if (recomputeUnlag)
{
// Clamp it to server time minus latency. Note that it could still
// be lower than previous, hence the clamp on the recomputation.
float newCommandTime = Clamp(serverTime - latencyAmount, lastCommandTime, serverTime);
cmd->command_time = newCommandTime;
DevWarning(eDLL_T::SERVER, "%s: Clamped cmd->command_time( %f ) to %f !!!\n",
__FUNCTION__, commandTime, newCommandTime);
}
CUserCmd* queuedCmd = &m_Commands[lastCommandNumber]; CUserCmd* queuedCmd = &m_Commands[lastCommandNumber];
queuedCmd->Copy(cmd); queuedCmd->Copy(cmd);

View File

@ -251,6 +251,9 @@ public:
void ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds, void ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds,
int droppedPackets, bool paused); int droppedPackets, bool paused);
void ClampUnlag(CUserCmd* cmd);
void PlayerRunCommand(CUserCmd* pUserCmd, IMoveHelper* pMover); void PlayerRunCommand(CUserCmd* pUserCmd, IMoveHelper* pMover);
void SetLastUserCommand(CUserCmd* pUserCmd); void SetLastUserCommand(CUserCmd* pUserCmd);