Added the old clock correction, new one caused flickering

This commit is contained in:
Kamay Xutax 2024-08-27 17:11:24 +02:00
parent a9b7bf8e2f
commit ad3b1ca178
7 changed files with 196 additions and 42 deletions

View file

@ -9,6 +9,7 @@
#include "client.h"
#include "client_pch.h"
#include "clockdriftmgr.h"
#include "sound.h"
#include <inetchannel.h>
#include "checksum_engine.h"
@ -508,6 +509,8 @@ Updates the local time and reads/handles messages on client net connection.
=================
*/
extern ConVar cl_clock_correction;
void CL_ReadPackets ( bool bFinalTick )
{
VPROF_BUDGET( "CL_ReadPackets", VPROF_BUDGETGROUP_OTHER_NETWORKING );
@ -517,26 +520,20 @@ void CL_ReadPackets ( bool bFinalTick )
return;
// update client times/tick
if (!CClockDriftMgr::IsClockCorrectionEnabled())
if (cl_clock_correction.GetInt() <= 1)
{
cl.oldtickcount = cl.GetServerTickCount();
}
// Moved after process socket so we can receive the lastest updates.
if ( !cl.IsPaused() )
{
// While clock correction is off, we have the old behavior of matching the client and server clocks.
if (!CClockDriftMgr::IsClockCorrectionEnabled())
if ( !cl.IsPaused() )
{
cl.SetClientTickCount(cl.GetClientTickCount() + 1);
cl.SetServerTickCount(cl.GetClientTickCount());
cl.SetClientTickCount( cl.GetClientTickCount() + 1 );
// While clock correction is off, we have the old behavior of matching the client and server clocks.
if ( !CClockDriftMgr::IsClockCorrectionEnabled() )
cl.SetServerTickCount( cl.GetClientTickCount() );
g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();
g_ClientGlobalVariables.curtime = cl.GetTime();
}
}
if (!CClockDriftMgr::IsClockCorrectionEnabled())
{
// 0 or tick_rate if simulating
g_ClientGlobalVariables.frametime = cl.GetFrameTime();
}
@ -583,29 +580,31 @@ void CL_ReadPackets ( bool bFinalTick )
}
#endif
if (CClockDriftMgr::IsClockCorrectionEnabled())
{
cl.oldtickcount = cl.GetServerTickCount();
}
// Moved after process socket so we can receive the lastest updates.
if ( !cl.IsPaused() )
{
// While clock correction is off, we have the old behavior of matching the client and server clocks.
if (cl_clock_correction.GetInt() >= 2)
{
if (CClockDriftMgr::IsClockCorrectionEnabled())
{
cl.m_ClockDriftMgr.IncrementCachedTickCount(bFinalTick);
g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();
g_ClientGlobalVariables.curtime = cl.GetTime();
cl.oldtickcount = cl.GetServerTickCount();
}
}
if (CClockDriftMgr::IsClockCorrectionEnabled())
{
// 0 or tick_rate if simulating
g_ClientGlobalVariables.frametime = cl.GetFrameTime();
}
// Moved after process socket so we can receive the lastest updates.
if ( !cl.IsPaused() )
{
// While clock correction is off, we have the old behavior of matching the client and server clocks.
if (CClockDriftMgr::IsClockCorrectionEnabled())
{
cl.m_ClockDriftMgr.IncrementCachedTickCount(bFinalTick);
g_ClientGlobalVariables.tickcount = cl.GetClientTickCount();
g_ClientGlobalVariables.curtime = cl.GetTime();
}
}
if (CClockDriftMgr::IsClockCorrectionEnabled())
{
// 0 or tick_rate if simulating
g_ClientGlobalVariables.frametime = cl.GetFrameTime();
}
}
}
//-----------------------------------------------------------------------------

View file

@ -11,7 +11,7 @@
#include "enginethreads.h"
ConVar cl_clock_correction( "cl_clock_correction", "0", FCVAR_CHEAT, "Enable/disable clock correction on the client." );
ConVar cl_clock_correction( "cl_clock_correction", "1", 0, "Enable/disable clock correction on the client." );
ConVar cl_clockdrift_max_ms( "cl_clockdrift_max_ms", "150", FCVAR_CHEAT, "Maximum number of milliseconds the clock is allowed to drift before the client snaps its clock to the server's." );
ConVar cl_clockdrift_max_ms_threadmode( "cl_clockdrift_max_ms_threadmode", "0", FCVAR_CHEAT, "Maximum number of milliseconds the clock is allowed to drift before the client snaps its clock to the server's." );
@ -19,7 +19,33 @@ ConVar cl_clockdrift_max_ms_threadmode( "cl_clockdrift_max_ms_threadmode", "0",
ConVar cl_clock_showdebuginfo( "cl_clock_showdebuginfo", "0", FCVAR_CHEAT, "Show debugging info about the clock drift. ");
ConVar cl_clock_correction_force_server_tick( "cl_clock_correction_force_server_tick", "999", FCVAR_CHEAT, "Force clock correction to match the server tick + this offset (-999 disables it)." );
ConVar cl_clock_correction_adjustment_max_amount( "cl_clock_correction_adjustment_max_amount", "200", FCVAR_CHEAT,
"Sets the maximum number of milliseconds per second it is allowed to correct the client clock. "
"It will only correct this amount if the difference between the client and server clock is equal to or larger than cl_clock_correction_adjustment_max_offset." );
ConVar cl_clock_correction_adjustment_min_offset( "cl_clock_correction_adjustment_min_offset", "10", FCVAR_CHEAT,
"If the clock offset is less than this amount (in milliseconds), then no clock correction is applied." );
ConVar cl_clock_correction_adjustment_max_offset( "cl_clock_correction_adjustment_max_offset", "90", FCVAR_CHEAT,
"As the clock offset goes from cl_clock_correction_adjustment_min_offset to this value (in milliseconds), "
"it moves towards applying cl_clock_correction_adjustment_max_amount of adjustment. That way, the response "
"is small when the offset is small." );
// Given the offset (in milliseconds) of the client clock from the server clock,
// returns how much correction we'd like to apply per second (in seconds).
static float GetClockAdjustmentAmount( float flCurDiffInMS )
{
flCurDiffInMS = clamp( flCurDiffInMS, cl_clock_correction_adjustment_min_offset.GetFloat(), cl_clock_correction_adjustment_max_offset.GetFloat() );
float flReturnValue = RemapVal( flCurDiffInMS,
cl_clock_correction_adjustment_min_offset.GetFloat(), cl_clock_correction_adjustment_max_offset.GetFloat(),
0, cl_clock_correction_adjustment_max_amount.GetFloat() / 1000.0f );
return flReturnValue;
}
// -------------------------------------------------------------------------------------------------- /
// CClockDriftMgr implementation.
@ -39,6 +65,9 @@ void CClockDriftMgr::Clear()
m_nLaggedClientTick = 0;
m_flServerHostFrametime = 0.0f;
m_flServerHostFrametimeStdDeviation = 0.0f;
m_iCurClockOffset = 0;
memset( m_ClockOffsets, 0, sizeof( m_ClockOffsets ) );
}
@ -65,8 +94,9 @@ void CClockDriftMgr::SetServerTick( int nTick, int nLaggedTick, float flServerHo
if (cl_clock_correction_force_server_tick.GetInt() == 999)
{
if (IsClockCorrectionEnabled())
{
// TODO_ENHANCED: !!!!!!!!! This needs to be corrected !!!!!!!!!!
if (IsClockCorrectionEnabled() && cl_clock_correction.GetInt() >= 2)
{
// Take the difference between last sent client tick and server tick
// This will give how much we are shifted from the server perfectly
@ -88,7 +118,14 @@ void CClockDriftMgr::SetServerTick( int nTick, int nLaggedTick, float flServerHo
{
// Used for testing..
m_nClientTick = (nTick + cl_clock_correction_force_server_tick.GetInt());
}
}
if (cl_clock_correction.GetInt() <= 1)
{
// adjust the clock offset by the clock with thread mode compensation
m_ClockOffsets[m_iCurClockOffset] = clientTick - m_nServerTick;
m_iCurClockOffset = (m_iCurClockOffset + 1) % NUM_CLOCKDRIFT_SAMPLES;
}
ShowDebugInfo();
m_nOldServerTick = m_nServerTick;
@ -105,6 +142,58 @@ void CClockDriftMgr::IncrementCachedTickCount(bool bFinalTick)
m_nClientTick = m_nCachedRealClientTick + m_nLagDiff;
}
float CClockDriftMgr::AdjustFrameTime( float inputFrameTime )
{
float flAdjustmentThisFrame = 0;
float flAdjustmentPerSec = 0;
if ( IsClockCorrectionEnabled()
#if !defined( _XBOX ) && !defined( SWDS )
&& !demoplayer->IsPlayingBack()
#endif
&& cl_clock_correction.GetInt() <= 1)
{
// Get the clock difference in seconds.
float flCurDiffInSeconds = GetCurrentClockDifference() * host_state.interval_per_tick;
float flCurDiffInMS = flCurDiffInSeconds * 1000.0f;
// Is the server ahead or behind us?
if ( flCurDiffInMS > cl_clock_correction_adjustment_min_offset.GetFloat() )
{
flAdjustmentPerSec = -GetClockAdjustmentAmount( flCurDiffInMS );
flAdjustmentThisFrame = inputFrameTime * flAdjustmentPerSec;
flAdjustmentThisFrame = max( flAdjustmentThisFrame, -flCurDiffInSeconds );
}
else if ( flCurDiffInMS < -cl_clock_correction_adjustment_min_offset.GetFloat() )
{
flAdjustmentPerSec = GetClockAdjustmentAmount( -flCurDiffInMS );
flAdjustmentThisFrame = inputFrameTime * flAdjustmentPerSec;
flAdjustmentThisFrame = min( flAdjustmentThisFrame, -flCurDiffInSeconds );
}
if ( IsEngineThreaded() )
{
flAdjustmentThisFrame = -flCurDiffInSeconds;
}
AdjustAverageDifferenceBy( flAdjustmentThisFrame );
}
return inputFrameTime + flAdjustmentThisFrame;
}
float CClockDriftMgr::GetCurrentClockDifference() const
{
// Note: this could be optimized a little by updating it each time we add
// a sample (subtract the old value from the total and add the new one in).
float total = 0;
for ( int i=0; i < NUM_CLOCKDRIFT_SAMPLES; i++ )
total += m_ClockOffsets[i];
return total / NUM_CLOCKDRIFT_SAMPLES;
}
void CClockDriftMgr::ShowDebugInfo()
{
#if !defined( SWDS )
@ -124,6 +213,22 @@ void CClockDriftMgr::ShowDebugInfo()
#endif
}
void CClockDriftMgr::AdjustAverageDifferenceBy( float flAmountInSeconds )
{
// Don't adjust the average if it's already tiny.
float c = GetCurrentClockDifference();
if ( c < 0.05f )
return;
float flAmountInTicks = flAmountInSeconds / host_state.interval_per_tick;
float factor = 1 + flAmountInTicks / c;
for ( int i=0; i < NUM_CLOCKDRIFT_SAMPLES; i++ )
m_ClockOffsets[i] *= factor;
Assert( fabs( GetCurrentClockDifference() - (c + flAmountInTicks) ) < 0.001f );
}
extern float NET_GetFakeLag();
extern ConVar net_usesocketsforloopback;

View file

@ -29,10 +29,32 @@ public:
void SetServerTick( int iServerTick, int nLaggedTick, float flServerHostFrametime, float flServerHostFrametimeStdDeviation);
void IncrementCachedTickCount(bool bFinalTick);
// Pass in the frametime you would use, and it will drift it towards the server clock.
float AdjustFrameTime( float inputFrameTime );
// Returns how many ticks ahead of the server the client is.
float GetCurrentClockDifference() const;
private:
void ShowDebugInfo();
// This scales the offsets so the average produced is equal to the
// current average + flAmount. This way, as we add corrections,
// we lower the average accordingly so we don't keep responding
// as much as we need to after we'd adjusted it a couple times.
void AdjustAverageDifferenceBy( float flAmountInSeconds );
enum
{
// This controls how much it smoothes out the samples from the server.
NUM_CLOCKDRIFT_SAMPLES=16
};
// This holds how many ticks the client is ahead each time we get a server tick.
// We average these together to get our estimate of how far ahead we are.
float m_ClockOffsets[NUM_CLOCKDRIFT_SAMPLES];
int m_iCurClockOffset;
public:
int m_nLagDiff;

View file

@ -1944,6 +1944,11 @@ void Host_AccumulateTime( float dt )
}
#endif
// Adjust the client clock very slightly to keep it in line with the server clock.
float adj = cl.GetClockDriftMgr().AdjustFrameTime( host_frametime ) - host_frametime;
host_frametime += adj;
host_frametime_unbounded += adj;
if ( g_pSoundServices ) // not present on linux server
g_pSoundServices->SetSoundFrametime(dt, host_frametime);
@ -3050,7 +3055,8 @@ void _Host_RunFrame (float time)
MDLCACHE_COARSE_LOCK_(g_pMDLCache);
static double host_remainder = 0.0f;
double prevremainder;
bool shouldrender;
bool shouldrender;
float angleframetime = 0.0f;
#if defined( RAD_TELEMETRY_ENABLED )
if( g_Telemetry.DemoTickEnd == ( uint32 )-1 )
@ -3411,6 +3417,9 @@ void _Host_RunFrame (float time)
CL_RunPrediction( PREDICTION_NORMAL );
CL_ApplyAddAngle();
// Ensure we get the right frametime
angleframetime = g_ClientGlobalVariables.frametime;
}
#endif
#if defined( REPLAY_ENABLED )
@ -3498,6 +3507,9 @@ void _Host_RunFrame (float time)
// This causes cl.gettime() to return the true clock being used for rendering (tickcount * rate + remainder)
Host_SetClientInSimulation( false );
// Ensure we get the right frametime
angleframetime = g_ClientGlobalVariables.frametime;
//-------------------
// Run prediction if it hasn't been run yet
//-------------------
@ -3666,15 +3678,16 @@ void _Host_RunFrame (float time)
} // Profile scope, protect from setjmp() problems
Host_ShowIPCCallCount();
#ifndef SWDS
// TODO_ENHANCED:
// Update the mouse as last so we can get the right prediction viewangles on a frame
// Update the mouse as last so we can get the right viewangles while taking screenshot.
// The mouse is always simulated for the current frame's time
// This makes updates smooth in every case
// continuous controllers affecting the view are also simulated this way
// but they have a cap applied by IN_SetSampleTime() so they are not also
// simulated during input gathering
CL_ExtraMouseUpdate( g_ClientGlobalVariables.frametime );
CL_ExtraMouseUpdate( angleframetime );
#endif
}
/*

View file

@ -149,7 +149,7 @@ extern int host_currentframetick;
// PERFORMANCE INFO
#define MIN_FPS 0.1 // Host minimum fps value for maxfps.
#define MAX_FPS 1000.0 // Upper limit for maxfps.
#define MAX_FPS 100000000.0 // Upper limit for maxfps.
#define MAX_FRAMETIME 0.1
#define MIN_FRAMETIME 0.001

View file

@ -279,6 +279,21 @@ bool CEngine::FilterTime( float dt )
return false;
}
}
else if ( fps == 0 )
{
m_flMinFrameTime = 0.0f;
if (
#if !defined(SWDS)
!demoplayer->IsPlayingTimeDemo() &&
#endif
!g_bDedicatedServerBenchmarkMode &&
dt < 0.0f )
{
// framerate is too high
return false;
}
}
return true;
}

View file

@ -767,7 +767,7 @@ C_CSPlayer::C_CSPlayer() :
{
m_angEyeAngles.Init();
// AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_ANIMATION_VAR );
// AddVar( &m_angEyeAngles, &m_iv_angEyeAngles, LATCH_SIMULATION_VAR );
m_iLastAddonBits = m_iAddonBits = 0;
m_iLastPrimaryAddon = m_iLastSecondaryAddon = WEAPON_NONE;