Fixed local player interpolation and added debug_screenshot_bullet_position

This check permits to fix interpolation problems on the
local player that valve has been (fucking finally)
caring about on counter-strike 2.

To recall the original issue, the
problem that Valve cared about is that interpolation
had some problems with interpolating the local
player because the screen would never in the first
place match the tick "screen", because interpolation
amount could never reach 0.0 or 1.0

Valve solution was to introduce bugs with lag
compensating the local player and made the game worse,
introducing a new way for cheaters to cheat even more
on their games.
I'm joking, but you can clearly see the outcome anyway.

My solution is to simply set interpolation amount
to 0.0 when a tick arrives.

So when we shoot, we get the frame we shot with an
interpolation amount at 0.0, perfectly aligned to user
commands which is ideal for us.

It might look a bit more unsmooth with lower fps
but with high enough fps, the issue goes away anyway.
It's not very noticeable which is very nice for us.
No need to lag compensate the local player anymore !
This commit is contained in:
Kamay Xutax 2024-07-14 00:50:53 +02:00
parent f8c641a9e5
commit cfa5b12eea
5 changed files with 103 additions and 25 deletions

View file

@ -15,6 +15,7 @@
#include "r_local.h"
#include "gl_lightmap.h"
#include "console.h"
#include "sv_main.h"
#include "traceinit.h"
#include "cl_demo.h"
#include "cdll_engine_int.h"
@ -1393,10 +1394,18 @@ void CL_TakeSnapshotAndSwap()
if ( bReadPixelsFromFrontBuffer )
{
Shader_SwapBuffers();
}
if (g_ClientGlobalVariables.client_taking_screenshot
|| g_ServerGlobalVariables.client_taking_screenshot)
{
CL_TakeScreenshot(NULL);
g_ClientGlobalVariables.client_taking_screenshot = false;
g_ServerGlobalVariables.client_taking_screenshot = false;
}
if (cl_takesnapshot)
{
{
// Disable threading for the duration of the screenshots, because we need to get pointers to the (complete)
// back buffer right now.
bool bEnabled = materials->AllowThreading( false, g_nMaterialSystemThread );
@ -1505,7 +1514,7 @@ void CL_TakeSnapshotAndSwap()
// Restore threading if it was previously enabled (if it wasn't this will do nothing).
materials->AllowThreading( bEnabled, g_nMaterialSystemThread );
}
}
// If recording movie and the console is totally up, then write out this frame to movie file.
if ( cl_movieinfo.IsRecording() && !Con_IsVisible() && !scr_drawloading )

View file

@ -3061,8 +3061,7 @@ void _Host_RunFrame (float time)
Cbuf_AddText( "quit\n" );
}
#endif
int numticks;
int numticks;
{
// Profile scope specific to the top of this function, protect from setjmp() problems
VPROF( "_Host_RunFrame_Upto_MarkFrame" );
@ -3236,7 +3235,7 @@ void _Host_RunFrame (float time)
//---------------------------------------------------------
// Run prediction, useful when fps is lower than tickrate.
//---------------------------------------------------------
CL_RunPrediction( PREDICTION_NORMAL );
// CL_RunPrediction( PREDICTION_NORMAL );
_Host_RunFrame_Input( prevremainder, bFinalTick );
prevremainder = 0;
@ -3310,24 +3309,72 @@ void _Host_RunFrame (float time)
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "Host_SetClientInSimulation" );
// This causes cl.gettime() to return the true clock being used for rendering (tickcount * rate + remainder)
Host_SetClientInSimulation( false );
// Now allow for interpolation on client
g_ClientGlobalVariables.interpolation_amount = ( cl.m_tickRemainder / host_state.interval_per_tick );
Host_SetClientInSimulation(false);
#if defined( REPLAY_ENABLED )
// Update client-side replay history manager - called here since interpolation_amount is set
if ( g_pClientReplayContext && g_pClientReplayContext->IsInitialized() )
// TODO_ENHANCED:
// Notice_Enhanced:
// This check permits to fix interpolation problems on the
// local player that valve has been (fucking finally)
// caring about on counter-strike 2.
//
// To recall the original issue, the
// problem that Valve cared about is that interpolation
// had some problems with interpolating the local
// player because the screen would never in the first
// place match the tick "screen", because interpolation
// amount could never reach 0.0 or 1.0
//
// Valve solution was to introduce bugs with lag
// compensating the local player and made the game worse,
// introducing a new way for cheaters to cheat even more
// on their games.
// I'm joking, but you can clearly see the outcome anyway.
//
// My solution is to simply set interpolation amount
// to 0.0 when a tick arrives.
//
// So when we shoot, we get the frame we shot with an
// interpolation amount at 0.0, perfectly aligned to user
// commands which is ideal for us.
//
// It might look a bit more unsmooth with lower fps
// but with high enough fps, the issue goes away anyway.
// It's not very noticeable which is very nice for us.
// No need to lag compensate the local player anymore !
if (numticks == 0)
{
g_ClientGlobalVariables.interpolation_amount = (cl.m_tickRemainder
/ host_state
.interval_per_tick);
}
else
{
g_ClientGlobalVariables.interpolation_amount = 0.0f;
#ifdef _DEBUG
printf("interpolation amount was %f, corrected to "
"fix interpolation issues.\n",
cl.m_tickRemainder
/ host_state.interval_per_tick);
#endif
}
#if defined(REPLAY_ENABLED)
// Update client-side replay history manager - called here
// since interpolation_amount is set
if ( g_pClientReplayContext && g_pClientReplayContext->IsInitialized() )
{
g_pClientReplayContext->Think();
}
#endif
#endif
//-------------------
// Run prediction if it hasn't been run yet
//-------------------
// If we haven't predicted/simulated the player (multiplayer with prediction enabled and
// not a listen server with zero frame lag, then go ahead and predict now
CL_RunPrediction( PREDICTION_NORMAL );
//-------------------
// Run prediction if it hasn't been run yet
//-------------------
// If we haven't predicted/simulated the player
// (multiplayer with prediction enabled and
// not a listen server with zero frame lag, then go ahead
// and predict now
CL_RunPrediction( PREDICTION_NORMAL );
CL_ApplyAddAngle();
@ -3417,9 +3464,24 @@ void _Host_RunFrame (float time)
// This causes cl.gettime() to return the true clock being used for rendering (tickcount * rate + remainder)
Host_SetClientInSimulation( false );
// Now allow for interpolation on client
g_ClientGlobalVariables.interpolation_amount = ( cl.m_tickRemainder / host_state.interval_per_tick );
// Please check Notice_Enhanced.
if (numticks == 0)
{
g_ClientGlobalVariables.interpolation_amount = (cl.m_tickRemainder
/ host_state
.interval_per_tick);
}
else
{
g_ClientGlobalVariables.interpolation_amount = 0.0f;
#ifdef _DEBUG
printf("interpolation amount was %f, corrected to "
"fix interpolation issues.\n",
cl.m_tickRemainder
/ host_state.interval_per_tick);
#endif
}
//-------------------
// Run prediction if it hasn't been run yet
//-------------------
@ -3443,7 +3505,7 @@ void _Host_RunFrame (float time)
g_ClientGlobalVariables.tickcount = host_tickcount;
bool bFinalTick = tick==(serverticks-1) ? true : false;
// Run prediction before inputs if fps is lower than tickrate
CL_RunPrediction( PREDICTION_NORMAL );
// CL_RunPrediction( PREDICTION_NORMAL );
_Host_RunFrame_Input( prevremainder, bFinalTick );
prevremainder = 0;
// process any asynchronous network traffic (TCP), set net_time
@ -3592,7 +3654,7 @@ void _Host_RunFrame (float time)
GetTestScriptMgr()->CheckPoint( "frame_end" );
} // Profile scope, protect from setjmp() problems
Host_ShowIPCCallCount();
Host_ShowIPCCallCount();
}
/*
==============================

View file

@ -922,7 +922,7 @@ void CPrediction::RunCommand( C_BasePlayer *player, CUserCmd *ucmd, IMoveHelper
// Get button states
player->UpdateButtonState( ucmd->buttons );
// CheckMovingGround( player, gpGlobals->frametime );
CheckMovingGround( player, gpGlobals->frametime );
g_pMoveData->m_vecOldAngles = player->pl.v_angle;

View file

@ -6,6 +6,7 @@
#include "cbase.h"
#include "fx_cs_shared.h"
#include "convar.h"
#include "weapon_csbase.h"
#ifndef CLIENT_DLL
@ -284,8 +285,12 @@ void FX_FireBullets(
y1[iBullet] = fRadius1 * sinf(fTheta1);
}
static ConVar debug_screenshot_bullet_position("debug_screenshot_bullet_position", "0");
for ( int iBullet=0; iBullet < pWeaponInfo->m_iBullets; iBullet++ )
{
{
if (debug_screenshot_bullet_position.GetBool())
gpGlobals->client_taking_screenshot = true;
pPlayer->FireBullet(
vOrigin,
vAngles,

View file

@ -74,6 +74,7 @@ public:
// current saverestore data
CSaveRestoreData *pSaveData;
bool client_taking_screenshot;
private:
// Set to true in client code.
@ -99,6 +100,7 @@ inline CGlobalVarsBase::CGlobalVarsBase( bool bIsClient ) :
nTimestampNetworkingBase( 100 ),
nTimestampRandomizeWindow( 32 )
{
client_taking_screenshot = false;
}
inline bool CGlobalVarsBase::IsClient() const