Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

238 lines
8.7 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. //=============================================================================//
  6. #include "client.h"
  7. #include "clockdriftmgr.h"
  8. #include "demo.h"
  9. #include "server.h"
  10. #include "enginethreads.h"
  11. // NOTE: This has to be the last file included!
  12. #include "tier0/memdbgon.h"
  13. ConVar cl_clock_correction( "cl_clock_correction", "1", FCVAR_CHEAT, "Enable/disable clock correction on the client." );
  14. 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." );
  15. 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." );
  16. ConVar cl_clock_showdebuginfo( "cl_clock_showdebuginfo", "0", FCVAR_CHEAT, "Show debugging info about the clock drift. ");
  17. 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)." );
  18. ConVar cl_clock_correction_adjustment_max_amount( "cl_clock_correction_adjustment_max_amount", "200", FCVAR_CHEAT,
  19. "Sets the maximum number of milliseconds per second it is allowed to correct the client clock. "
  20. "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." );
  21. ConVar cl_clock_correction_adjustment_min_offset( "cl_clock_correction_adjustment_min_offset", "10", FCVAR_CHEAT,
  22. "If the clock offset is less than this amount (in milliseconds), then no clock correction is applied." );
  23. ConVar cl_clock_correction_adjustment_max_offset( "cl_clock_correction_adjustment_max_offset", "90", FCVAR_CHEAT,
  24. "As the clock offset goes from cl_clock_correction_adjustment_min_offset to this value (in milliseconds), "
  25. "it moves towards applying cl_clock_correction_adjustment_max_amount of adjustment. That way, the response "
  26. "is small when the offset is small." );
  27. // Given the offset (in milliseconds) of the client clock from the server clock,
  28. // returns how much correction we'd like to apply per second (in seconds).
  29. static float GetClockAdjustmentAmount( float flCurDiffInMS )
  30. {
  31. flCurDiffInMS = clamp( flCurDiffInMS, cl_clock_correction_adjustment_min_offset.GetFloat(), cl_clock_correction_adjustment_max_offset.GetFloat() );
  32. float flReturnValue = RemapVal( flCurDiffInMS,
  33. cl_clock_correction_adjustment_min_offset.GetFloat(), cl_clock_correction_adjustment_max_offset.GetFloat(),
  34. 0, cl_clock_correction_adjustment_max_amount.GetFloat() / 1000.0f );
  35. return flReturnValue;
  36. }
  37. // -------------------------------------------------------------------------------------------------- /
  38. // CClockDriftMgr implementation.
  39. // -------------------------------------------------------------------------------------------------- /
  40. CClockDriftMgr::CClockDriftMgr()
  41. {
  42. Clear();
  43. }
  44. void CClockDriftMgr::Clear()
  45. {
  46. m_nClientTick = 0;
  47. m_nServerTick = 0;
  48. m_iCurClockOffset = 0;
  49. memset( m_ClockOffsets, 0, sizeof( m_ClockOffsets ) );
  50. }
  51. // when running in threaded host mode, the clock drifts by a predictable algorithm
  52. // because the client lags the server by one frame
  53. // so at each update from the network we have lastframeticks-1 pending ticks to execute
  54. // on the client. If the clock has drifted by exactly that amount, allow it to drift temporarily
  55. // NOTE: When the server gets paused the tick count is still incorrect for a frame
  56. // NOTE: It should be possible to fix this by applying pause before the tick is incremented
  57. // NOTE: or decrementing the client tick after receiving pause
  58. // NOTE: This is due to the fact that currently pause is applied at frame start on the server
  59. // NOTE: and frame end on the client
  60. void CClockDriftMgr::SetServerTick( int nTick )
  61. {
  62. #if !defined( DEDICATED )
  63. m_nServerTick = nTick;
  64. int nMaxDriftTicks = IsEngineThreaded() ?
  65. TIME_TO_TICKS( (cl_clockdrift_max_ms_threadmode.GetFloat() / 1000.0) ) :
  66. TIME_TO_TICKS( (cl_clockdrift_max_ms.GetFloat() / 1000.0) );
  67. int clientTick = GetBaseLocalClient().GetClientTickCount() + g_ClientGlobalVariables.simTicksThisFrame - 1;
  68. if ( cl_clock_correction_force_server_tick.GetInt() == 999 )
  69. {
  70. // If this is the first tick from the server, or if we get further than cl_clockdrift_max_ticks off, then
  71. // use the old behavior and slam the server's tick into the client tick.
  72. if ( !IsClockCorrectionEnabled() ||
  73. clientTick == 0 ||
  74. abs(nTick - clientTick) > nMaxDriftTicks ||
  75. ( demoplayer && demoplayer->IsPlayingBack() && clientTick > nTick )
  76. )
  77. {
  78. GetBaseLocalClient().SetClientTickCount( nTick - (g_ClientGlobalVariables.simTicksThisFrame - 1) );
  79. if ( GetBaseLocalClient().GetClientTickCount() < GetBaseLocalClient().oldtickcount )
  80. {
  81. GetBaseLocalClient().oldtickcount = GetBaseLocalClient().GetClientTickCount();
  82. }
  83. memset( m_ClockOffsets, 0, sizeof( m_ClockOffsets ) );
  84. }
  85. }
  86. else
  87. {
  88. // Used for testing..
  89. GetBaseLocalClient().SetClientTickCount( nTick + cl_clock_correction_force_server_tick.GetInt() );
  90. }
  91. // adjust the clock offset by the clock with thread mode compensation
  92. m_ClockOffsets[m_iCurClockOffset] = clientTick - m_nServerTick;
  93. m_iCurClockOffset = (m_iCurClockOffset + 1) % NUM_CLOCKDRIFT_SAMPLES;
  94. #endif // DEDICATED
  95. }
  96. float CClockDriftMgr::AdjustFrameTime( float inputFrameTime )
  97. {
  98. float flAdjustmentThisFrame = 0;
  99. float flAdjustmentPerSec = 0;
  100. if ( IsClockCorrectionEnabled()
  101. #if !defined( DEDICATED )
  102. && !demoplayer->IsPlayingBack()
  103. #endif
  104. )
  105. {
  106. // Get the clock difference in seconds.
  107. float flCurDiffInSeconds = GetCurrentClockDifference() * host_state.interval_per_tick;
  108. float flCurDiffInMS = flCurDiffInSeconds * 1000.0f;
  109. // Is the server ahead or behind us?
  110. if ( flCurDiffInMS > cl_clock_correction_adjustment_min_offset.GetFloat() )
  111. {
  112. flAdjustmentPerSec = -GetClockAdjustmentAmount( flCurDiffInMS );
  113. flAdjustmentThisFrame = inputFrameTime * flAdjustmentPerSec;
  114. flAdjustmentThisFrame = MAX( flAdjustmentThisFrame, -flCurDiffInSeconds );
  115. }
  116. else if ( flCurDiffInMS < -cl_clock_correction_adjustment_min_offset.GetFloat() )
  117. {
  118. flAdjustmentPerSec = GetClockAdjustmentAmount( -flCurDiffInMS );
  119. flAdjustmentThisFrame = inputFrameTime * flAdjustmentPerSec;
  120. flAdjustmentThisFrame = MIN( flAdjustmentThisFrame, -flCurDiffInSeconds );
  121. }
  122. if ( IsEngineThreaded() )
  123. {
  124. flAdjustmentThisFrame = -flCurDiffInSeconds;
  125. }
  126. AdjustAverageDifferenceBy( flAdjustmentThisFrame );
  127. }
  128. ShowDebugInfo( flAdjustmentPerSec );
  129. return inputFrameTime + flAdjustmentThisFrame;
  130. }
  131. float CClockDriftMgr::GetCurrentClockDifference() const
  132. {
  133. // Note: this could be optimized a little by updating it each time we add
  134. // a sample (subtract the old value from the total and add the new one in).
  135. float total = 0;
  136. for ( int i=0; i < NUM_CLOCKDRIFT_SAMPLES; i++ )
  137. total += m_ClockOffsets[i];
  138. return total / NUM_CLOCKDRIFT_SAMPLES;
  139. }
  140. void CClockDriftMgr::ShowDebugInfo( float flAdjustment )
  141. {
  142. #ifndef DEDICATED
  143. if ( !cl_clock_showdebuginfo.GetInt() )
  144. return;
  145. #ifndef DEDICATED
  146. if ( IsClockCorrectionEnabled() )
  147. {
  148. int high=-999, low=999;
  149. int exactDiff = GetBaseLocalClient().GetClientTickCount() - m_nServerTick;
  150. for ( int i=0; i < NUM_CLOCKDRIFT_SAMPLES; i++ )
  151. {
  152. high = MAX( high, m_ClockOffsets[i] );
  153. low = MIN( low, m_ClockOffsets[i] );
  154. }
  155. Msg( "Clock drift: adjustment (per sec): %.2fms, avg: %.3f, lo: %d, hi: %d, ex: %d\n", flAdjustment*1000.0f, GetCurrentClockDifference(), low, high, exactDiff );
  156. }
  157. else
  158. #endif
  159. {
  160. Msg( "Clock drift disabled.\n" );
  161. }
  162. #endif
  163. }
  164. void CClockDriftMgr::AdjustAverageDifferenceBy( float flAmountInSeconds )
  165. {
  166. // Don't adjust the average if it's already tiny.
  167. float c = GetCurrentClockDifference();
  168. if ( c < 0.05f )
  169. return;
  170. float flAmountInTicks = flAmountInSeconds / host_state.interval_per_tick;
  171. float factor = 1 + flAmountInTicks / c;
  172. for ( int i=0; i < NUM_CLOCKDRIFT_SAMPLES; i++ )
  173. m_ClockOffsets[i] *= factor;
  174. Assert( fabs( GetCurrentClockDifference() - (c + flAmountInTicks) ) < 0.001f );
  175. }
  176. extern float NET_GetFakeLag();
  177. extern ConVar net_usesocketsforloopback;
  178. bool CClockDriftMgr::IsClockCorrectionEnabled()
  179. {
  180. #ifdef DEDICATED
  181. return false;
  182. #else
  183. if ( sv.IsActive() && ( IsGameConsole() || NET_GetFakeLag() <= 0.0f ) )
  184. {
  185. // Never want this in listen server. Has the result of slamming the server time
  186. // as well in host.cpp, thus can never recover. Yields the server simulation running
  187. // noticably faster until a full network update is triggered. [3/31/2009 tom]
  188. return false;
  189. }
  190. return cl_clock_correction.GetBool();
  191. #endif
  192. }