Finish unlag exploit fix

- Make sure cmdTime is never lower than last.
- Make sure delta doesn't exceed maxUnlag.
- Make sure latency gets clamped to maxUnlag (clamp appears to be missing in the engine).
- Only check for cmdTime if we are not discarding the cmd; previously, already processed queued cmd's got checked before being discarded.
This commit is contained in:
Amos 2023-08-15 00:45:58 +02:00
parent 631e6d9e17
commit 3ff85c174a
3 changed files with 43 additions and 28 deletions

View File

@ -104,7 +104,7 @@ ConVar* sv_forceChatToTeamOnly = nullptr;
ConVar* sv_single_core_dedi = nullptr;
ConVar* sv_maxunlag_delta = nullptr;
ConVar* sv_maxunlag = nullptr;
ConVar* sv_updaterate_sp = nullptr;
ConVar* sv_updaterate_mp = nullptr;
@ -328,8 +328,6 @@ void ConVar_StaticInit(void)
sv_autoReloadRate = ConVar::StaticCreate("sv_autoReloadRate" , "0" , FCVAR_RELEASE, "Time in seconds between each server auto-reload (disabled if null).", true, 0.f, false, 0.f, nullptr, nullptr);
sv_simulateBots = ConVar::StaticCreate("sv_simulateBots", "1", FCVAR_RELEASE, "Simulate user commands for bots on the server.", true, 0.f, false, 0.f, nullptr, nullptr);
sv_maxunlag_delta = ConVar::StaticCreate("sv_maxunlag_delta", "0.3", FCVAR_RELEASE, "Maximum amount of deviation in seconds between server and UserCmd's issue time for unlag (excludes latency).", true, 0.f, true, 1.f, nullptr, nullptr);
sv_rcon_debug = ConVar::StaticCreate("sv_rcon_debug" , "0" , FCVAR_RELEASE, "Show rcon debug information ( !slower! ).", false, 0.f, false, 0.f, nullptr, nullptr);
sv_rcon_sendlogs = ConVar::StaticCreate("sv_rcon_sendlogs" , "0" , FCVAR_RELEASE, "Network console logs to connected and authenticated sockets.", false, 0.f, false, 0.f, nullptr, nullptr);
//sv_rcon_banpenalty = ConVar::StaticCreate("sv_rcon_banpenalty" , "10", FCVAR_RELEASE, "Number of minutes to ban users who fail rcon authentication.", false, 0.f, false, 0.f, nullptr, nullptr);
@ -520,6 +518,8 @@ void ConVar_InitShipped(void)
#ifndef CLIENT_DLL
sv_stats = g_pCVar->FindVar("sv_stats");
sv_maxunlag = g_pCVar->FindVar("sv_maxunlag");
sv_updaterate_sp = g_pCVar->FindVar("sv_updaterate_sp");
sv_updaterate_mp = g_pCVar->FindVar("sv_updaterate_mp");

View File

@ -93,7 +93,7 @@ extern ConVar* sv_forceChatToTeamOnly;
extern ConVar* sv_single_core_dedi;
extern ConVar* sv_maxunlag_delta;
extern ConVar* sv_maxunlag;
extern ConVar* sv_updaterate_sp;
extern ConVar* sv_updaterate_mp;

View File

@ -125,49 +125,64 @@ void CPlayer::ProcessUserCmds(CUserCmd* cmds, int numCmds, int totalCmds,
CUserCmd* lastCmd = &m_Commands[MAX_QUEUED_COMMANDS_PROCESS];
const float latencyAmount = chan->GetLatency(FLOW_OUTGOING);
const float maxCommandDelta = sv_maxunlag_delta->GetFloat();
const float maxUnlag = sv_maxunlag->GetFloat();
const float latencyAmount = Clamp(chan->GetLatency(FLOW_OUTGOING), 0.0f, maxUnlag);
const float localCurTime = (*g_pGlobals)->m_flCurTime;
const float lastCommandTime = m_LastCmd.command_time;
for (int i = totalCmds - 1; i >= 0; i--)
{
CUserCmd* cmd = &cmds[i];
const int commandNumber = cmd->command_number;
if (commandNumber <= m_latestCommandQueued)
continue;
m_latestCommandQueued = commandNumber;
const int lastCommandNumber = lastCmd->command_number;
if (lastCommandNumber == MAX_QUEUED_COMMANDS_PROCESS)
return;
// 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 commandDelta = fabs(commandTime - localCurTime) - latencyAmount;
const float lastCommandTime = m_LastCmd.command_time;
if (/*commandTime < lastCommandTime ||*/ commandDelta > maxCommandDelta)
if (commandTime < lastCommandTime) // Can never be lower than last !!!
{
cmd->command_time = localCurTime - latencyAmount;
if (IsDebug())
{
Warning(eDLL_T::SERVER, "%s: Command delta of %f exceeded maximum of %f; commandTime=%f, lastCommandTime=%f, localCurTime=%f, latencyAmount=%f\n",
__FUNCTION__, commandDelta, maxCommandDelta, commandTime, lastCommandTime, localCurTime, latencyAmount);
Warning(eDLL_T::SERVER, "%s: cmd->command_time( %f ) < m_LastCmd.command_time( %f ); clamped to %f !!!\n",
__FUNCTION__, commandTime, lastCommandTime, cmd->command_time);
}
}
else
{
const float commandDelta = fabs(commandTime - localCurTime);
if (commandDelta > maxUnlag) // Too much to unlag, clamp to max !!!
{
cmd->command_time = localCurTime - latencyAmount;
if (IsDebug())
{
Warning(eDLL_T::SERVER, "%s: commandDelta( %f ) > maxUnlag( %f ); clamped to %f !!!\n",
__FUNCTION__, commandDelta, maxUnlag, cmd->command_time);
}
}
}
const int commandNumber = cmd->command_number;
CUserCmd* queuedCmd = &m_Commands[lastCommandNumber];
queuedCmd->Copy(cmd);
if (commandNumber > m_latestCommandQueued)
if (++lastCmd->command_number > player_userCmdsQueueWarning->GetInt())
{
m_latestCommandQueued = commandNumber;
const int lastCommandNumber = lastCmd->command_number;
const float curTime = float(Plat_FloatTime());
if (lastCommandNumber == MAX_QUEUED_COMMANDS_PROCESS)
return;
CUserCmd* queuedCmd = &m_Commands[lastCommandNumber];
queuedCmd->Copy(cmd);
if (++lastCmd->command_number > player_userCmdsQueueWarning->GetInt())
{
const float curTime = float(Plat_FloatTime());
if ((curTime - m_lastCommandCountWarnTime) > 0.5f)
m_lastCommandCountWarnTime = curTime;
}
if ((curTime - m_lastCommandCountWarnTime) > 0.5f)
m_lastCommandCountWarnTime = curTime;
}
}