mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
Fix >190FPS input system/simulation problems
Function 'CL_Move' has been fully rebuild in the SDK. Originally, the game checked if the delta time exceeded an amount defined by an immediate value, and dropped usercmd's if that was the case. This logic has been replaced with a more dynamic solution, and the console variable regulating this ('fps_input_max') is set to 200.0 by default (the same as the fix applied in the Season 9.1 Genesis update). This function also has been slightly optimized by removing duplicate operations that were performed in the original function. A second fix has been applied to 'CInput::JoyStickApplyMovement' that was also found changed in the Season 9.1 Genesis executable. In that function, an extraneous clamp was performed on the frame time causing viewstick problems when usercmd's get dropped in CL_Move.
This commit is contained in:
parent
b567d8d74f
commit
1afa75fec3
@ -21,6 +21,7 @@ ConVar* debug_draw_box_depth_test = nullptr;
|
||||
|
||||
ConVar* developer = nullptr;
|
||||
ConVar* fps_max = nullptr;
|
||||
ConVar* fps_input_max = nullptr;
|
||||
|
||||
ConVar* staticProp_defaultBuildFrustum = nullptr;
|
||||
ConVar* staticProp_no_fade_scalar = nullptr;
|
||||
@ -34,7 +35,10 @@ ConVar* hostname = nullptr;
|
||||
ConVar* hostdesc = nullptr;
|
||||
ConVar* hostip = nullptr;
|
||||
ConVar* hostport = nullptr;
|
||||
|
||||
ConVar* host_hasIrreversibleShutdown = nullptr;
|
||||
ConVar* host_timescale = nullptr;
|
||||
|
||||
ConVar* mp_gamemode = nullptr;
|
||||
|
||||
ConVar* rcon_address = nullptr;
|
||||
@ -128,6 +132,9 @@ ConVar* bhit_abs_origin = nullptr;
|
||||
ConVar* cl_rcon_request_sendlogs = nullptr;
|
||||
ConVar* cl_quota_stringCmdsPerSecond = nullptr;
|
||||
|
||||
ConVar* cl_cmdrate = nullptr;
|
||||
ConVar* cl_move_use_dt = nullptr;
|
||||
|
||||
ConVar* cl_notify_invert_x = nullptr;
|
||||
ConVar* cl_notify_invert_y = nullptr;
|
||||
ConVar* cl_notify_offset_x = nullptr;
|
||||
@ -372,6 +379,8 @@ void ConVar_StaticInit(void)
|
||||
serverbrowser_hideEmptyServers = ConVar::StaticCreate("serverbrowser_hideEmptyServers", "0", FCVAR_RELEASE, "Hide empty servers in the server browser", false, 0.f, false, 0.f, nullptr, nullptr);
|
||||
serverbrowser_mapFilter = ConVar::StaticCreate("serverbrowser_mapFilter", "0", FCVAR_RELEASE, "Filter servers by map in the server browser", false, 0.f, false, 0.f, nullptr, nullptr);
|
||||
serverbrowser_gamemodeFilter = ConVar::StaticCreate("serverbrowser_gamemodeFilter", "0", FCVAR_RELEASE, "Filter servers by gamemode in the server browser", false, 0.f, false, 0.f, nullptr, nullptr);
|
||||
|
||||
fps_input_max = ConVar::StaticCreate("fps_input_max", "200.0", FCVAR_RELEASE, "Max movement updates ran per second.", false, 0.f, false, 0.f, nullptr, nullptr);
|
||||
#endif // !DEDICATED
|
||||
//-------------------------------------------------------------------------
|
||||
// FILESYSTEM |
|
||||
@ -431,6 +440,8 @@ void ConVar_InitShipped(void)
|
||||
fps_max = g_pCVar->FindVar("fps_max");
|
||||
fs_showAllReads = g_pCVar->FindVar("fs_showAllReads");
|
||||
#ifndef DEDICATED
|
||||
cl_cmdrate = g_pCVar->FindVar("cl_cmdrate");
|
||||
cl_move_use_dt = g_pCVar->FindVar("cl_move_use_dt");
|
||||
cl_threaded_bone_setup = g_pCVar->FindVar("cl_threaded_bone_setup");
|
||||
#endif // !DEDICATED
|
||||
single_frame_shutdown_for_reload = g_pCVar->FindVar("single_frame_shutdown_for_reload");
|
||||
@ -461,6 +472,7 @@ void ConVar_InitShipped(void)
|
||||
hostip = g_pCVar->FindVar("hostip");
|
||||
hostport = g_pCVar->FindVar("hostport");
|
||||
host_hasIrreversibleShutdown = g_pCVar->FindVar("host_hasIrreversibleShutdown");
|
||||
host_timescale = g_pCVar->FindVar("host_timescale");
|
||||
net_usesocketsforloopback = g_pCVar->FindVar("net_usesocketsforloopback");
|
||||
#ifndef CLIENT_DLL
|
||||
sv_stats = g_pCVar->FindVar("sv_stats");
|
||||
|
@ -12,6 +12,7 @@ extern ConVar* debug_draw_box_depth_test;
|
||||
|
||||
extern ConVar* developer;
|
||||
extern ConVar* fps_max;
|
||||
extern ConVar* fps_input_max;
|
||||
|
||||
extern ConVar* staticProp_defaultBuildFrustum;
|
||||
extern ConVar* staticProp_no_fade_scalar;
|
||||
@ -25,7 +26,9 @@ extern ConVar* hostname;
|
||||
extern ConVar* hostdesc;
|
||||
extern ConVar* hostip;
|
||||
extern ConVar* hostport;
|
||||
|
||||
extern ConVar* host_hasIrreversibleShutdown;
|
||||
extern ConVar* host_timescale;
|
||||
|
||||
extern ConVar* mp_gamemode;
|
||||
|
||||
@ -119,6 +122,9 @@ extern ConVar* bhit_abs_origin;
|
||||
extern ConVar* cl_rcon_request_sendlogs;
|
||||
extern ConVar* cl_quota_stringCmdsPerSecond;
|
||||
|
||||
extern ConVar* cl_cmdrate;
|
||||
extern ConVar* cl_move_use_dt;
|
||||
|
||||
extern ConVar* cl_notify_invert_x;
|
||||
extern ConVar* cl_notify_invert_y;
|
||||
extern ConVar* cl_notify_offset_x;
|
||||
|
@ -117,6 +117,7 @@ add_sources( SOURCE_GROUP "Client"
|
||||
"client/cdll_engine_int.h"
|
||||
"client/cl_ents_parse.cpp"
|
||||
"client/cl_ents_parse.h"
|
||||
"client/cl_main.cpp"
|
||||
"client/cl_main.h"
|
||||
"client/cl_splitscreen.cpp"
|
||||
"client/cl_splitscreen.h"
|
||||
|
137
r5dev/engine/client/cl_main.cpp
Normal file
137
r5dev/engine/client/cl_main.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
//=============================================================================//
|
||||
//
|
||||
// Purpose:
|
||||
//
|
||||
//=============================================================================//
|
||||
|
||||
#include "engine/host.h"
|
||||
#include "clientstate.h"
|
||||
#include "cl_splitscreen.h"
|
||||
#include "cl_main.h"
|
||||
#include "engine/net.h"
|
||||
#include "cdll_engine_int.h"
|
||||
|
||||
static float s_lastMovementCall = 0.0;
|
||||
static float s_LastFrameTime = 0.0;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: run client's movement frame
|
||||
//-----------------------------------------------------------------------------
|
||||
void H_CL_Move()
|
||||
{
|
||||
CClientState* cl = GetBaseLocalClient();
|
||||
|
||||
if (!cl->IsConnected())
|
||||
return;
|
||||
|
||||
if (!v_Host_ShouldRun())
|
||||
return;
|
||||
|
||||
int commandTick = -1;
|
||||
|
||||
if (cl->m_CurrFrameSnapshot)
|
||||
commandTick = cl->m_CurrFrameSnapshot->m_TickUpdate.m_nCommandTick;
|
||||
|
||||
bool sendPacket = true;
|
||||
float netTime = float(*g_pNetTime);
|
||||
CNetChan* chan = cl->m_NetChannel;
|
||||
|
||||
if (cl->m_flNextCmdTime <= (0.5 / cl_cmdrate->GetFloat()) + netTime)
|
||||
sendPacket = chan->CanPacket();
|
||||
|
||||
else if (g_pClientState->m_nOutgoingCommandNr - (commandTick+1) < 15 || host_timescale->GetFloat() == 1.0)
|
||||
sendPacket = false;
|
||||
|
||||
if (cl->IsActive())
|
||||
{
|
||||
float timeNow = float(Plat_FloatTime());
|
||||
int outCommandNr = g_pClientState->m_nOutgoingCommandNr;
|
||||
|
||||
bool isPaused = g_pClientState->IsPaused();
|
||||
int nextCommandNr = isPaused ? outCommandNr : outCommandNr+1;
|
||||
|
||||
FOR_EACH_SPLITSCREEN_PLAYER(i)
|
||||
{
|
||||
if (g_pSplitScreenMgr->IsDisconnecting(i))
|
||||
continue;
|
||||
|
||||
float frameTime = 0.0f;
|
||||
|
||||
if (cl_move_use_dt->GetBool())
|
||||
{
|
||||
float timeScale;
|
||||
float deltaTime;
|
||||
|
||||
if (isPaused)
|
||||
{
|
||||
timeScale = 1.0f;
|
||||
frameTime = timeNow - s_lastMovementCall;
|
||||
deltaTime = frameTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
timeScale = host_timescale->GetFloat();
|
||||
frameTime = cl->m_flFrameTime + s_LastFrameTime;
|
||||
deltaTime = frameTime / timeScale;
|
||||
}
|
||||
|
||||
if (deltaTime > 0.1f)
|
||||
frameTime = timeScale * 0.1f;
|
||||
|
||||
// This check originally was 'time < 0.0049999999', but
|
||||
// that caused problems when the framerate was above 190.
|
||||
if ((1.0 / fps_input_max->GetFloat()) > deltaTime)
|
||||
{
|
||||
s_LastFrameTime = frameTime;
|
||||
return;
|
||||
}
|
||||
|
||||
s_LastFrameTime = 0.0;
|
||||
}
|
||||
//else if (isPaused)
|
||||
//{
|
||||
// // This hlClient virtual call just returns false.
|
||||
//}
|
||||
|
||||
// Create and store usercmd structure.
|
||||
g_pHLClient->CreateMove(nextCommandNr, frameTime, !isPaused);
|
||||
g_pClientState->m_nOutgoingCommandNr = nextCommandNr;
|
||||
}
|
||||
|
||||
CL_RunPrediction();
|
||||
|
||||
if (sendPacket)
|
||||
{
|
||||
CL_SendMove();
|
||||
|
||||
CLC_ClientTick tickMsg(cl->m_nDeltaTick, cl->m_nStringTableAckTick);
|
||||
|
||||
chan->SendNetMsg(tickMsg, false, false);
|
||||
chan->SendDatagram(nullptr);
|
||||
|
||||
// Use full update rate when active.
|
||||
float delta = netTime - float(g_pClientState->m_flNextCmdTime);
|
||||
float commandInterval = (1.0f / cl_cmdrate->GetFloat()) - 0.001f;
|
||||
float maxDelta = 0.0f;
|
||||
|
||||
if (delta >= 0.0f)
|
||||
maxDelta = fminf(commandInterval, delta);
|
||||
|
||||
g_pClientState->m_flNextCmdTime = double(commandInterval + netTime - maxDelta);
|
||||
}
|
||||
else // Choke the packet...
|
||||
chan->SetChoked();
|
||||
|
||||
s_lastMovementCall = timeNow;
|
||||
}
|
||||
}
|
||||
|
||||
void VCL_Main::Attach() const
|
||||
{
|
||||
DetourAttach(&CL_Move, &H_CL_Move);
|
||||
}
|
||||
|
||||
void VCL_Main::Detach() const
|
||||
{
|
||||
DetourDetach(&CL_Move, &H_CL_Move);
|
||||
}
|
@ -1,38 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// RUNTIME: CL_CLEARSTATE
|
||||
//-------------------------------------------------------------------------
|
||||
inline CMemory p_CL_ClearState;
|
||||
inline auto CL_ClearState = p_CL_ClearState.RCast<int(*)(void)>();
|
||||
inline CMemory p_CL_Move;
|
||||
inline auto CL_Move = p_CL_Move.RCast<void(*)(void)>();
|
||||
|
||||
inline CMemory p_CL_SendMove;
|
||||
inline auto CL_SendMove = p_CL_SendMove.RCast<void(*)(void)>();
|
||||
|
||||
inline CMemory p_CL_EndMovie;
|
||||
inline auto CL_EndMovie = p_CL_EndMovie.RCast<int(*)(void)>();
|
||||
|
||||
inline CMemory p_CL_ClearState;
|
||||
inline auto CL_ClearState = p_CL_ClearState.RCast<int(*)(void)>();
|
||||
|
||||
inline CMemory p_CL_RunPrediction;
|
||||
inline auto CL_RunPrediction = p_CL_RunPrediction.RCast<void(*)(void)>();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
class VCL_Main : public IDetour
|
||||
{
|
||||
virtual void GetAdr(void) const
|
||||
{
|
||||
LogFunAdr("CL_Move", p_CL_Move.GetPtr());
|
||||
LogFunAdr("CL_SendMove", p_CL_SendMove.GetPtr());
|
||||
LogFunAdr("CL_EndMovie", p_CL_EndMovie.GetPtr());
|
||||
LogFunAdr("CL_ClearState", p_CL_ClearState.GetPtr());
|
||||
LogFunAdr("CL_RunPrediction", p_CL_RunPrediction.GetPtr());
|
||||
}
|
||||
virtual void GetFun(void) const
|
||||
{
|
||||
#if defined (GAMEDLL_S0) || defined (GAMEDLL_S1)
|
||||
p_CL_ClearState = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 1D ?? ?? ?? ??");
|
||||
p_CL_Move = g_GameDll.FindPatternSIMD("40 53 48 81 EC ?? ?? ?? ?? 83 3D ?? ?? ?? ?? ?? 0F B6 DA");
|
||||
p_CL_SendMove = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 55 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ??");
|
||||
p_CL_EndMovie = g_GameDll.FindPatternSIMD("48 8B C4 48 83 EC 68 80 3D ?? ?? ?? ?? ??");
|
||||
p_CL_ClearState = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 1D ?? ?? ?? ??");
|
||||
p_CL_RunPrediction = g_GameDll.FindPatternSIMD("4C 8B DC 48 83 EC 58 83 3D ?? ?? ?? ?? ??");
|
||||
#elif defined (GAMEDLL_S2) || defined (GAMEDLL_S3)
|
||||
p_CL_ClearState = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01");
|
||||
p_CL_Move = g_GameDll.FindPatternSIMD("48 81 EC ?? ?? ?? ?? 83 3D ?? ?? ?? ?? ?? 44 0F 29 5C 24 ??");
|
||||
p_CL_SendMove = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 05 ?? ?? ?? ??");
|
||||
p_CL_EndMovie = g_GameDll.FindPatternSIMD("48 83 EC 28 80 3D ?? ?? ?? ?? ?? 74 7B");
|
||||
p_CL_ClearState = g_GameDll.FindPatternSIMD("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01");
|
||||
p_CL_RunPrediction = g_GameDll.FindPatternSIMD("48 83 EC 48 83 3D ?? ?? ?? ?? ?? 0F 85 ?? ?? ?? ?? 80 3D ?? ?? ?? ?? ??");
|
||||
#endif
|
||||
CL_ClearState = p_CL_ClearState.RCast<int(*)(void)>(); /*48 89 5C 24 ?? 48 89 74 24 ?? 57 48 81 EC ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? 48 8B 01*/
|
||||
CL_EndMovie = p_CL_EndMovie.RCast<int(*)(void)>(); /*48 83 EC 28 80 3D ?? ?? ?? ?? ?? 74 7B*/
|
||||
CL_Move = p_CL_Move.RCast<void(*)(void)>();
|
||||
CL_SendMove = p_CL_SendMove.RCast<void(*)(void)>();
|
||||
CL_EndMovie = p_CL_EndMovie.RCast<int(*)(void)>();
|
||||
CL_ClearState = p_CL_ClearState.RCast<int(*)(void)>();
|
||||
CL_RunPrediction = p_CL_RunPrediction.RCast<void(*)(void)>();
|
||||
}
|
||||
virtual void GetVar(void) const { }
|
||||
virtual void GetCon(void) const { }
|
||||
virtual void Attach(void) const { }
|
||||
virtual void Detach(void) const { }
|
||||
virtual void Attach(void) const;
|
||||
virtual void Detach(void) const;
|
||||
};
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -119,6 +119,11 @@ private:
|
||||
#define IS_VALID_SPLIT_SCREEN_SLOT( i ) ( g_pSplitScreenMgr->IsValidSplitScreenSlot( i ) )
|
||||
#endif
|
||||
|
||||
inline CClientState* GetBaseLocalClient()
|
||||
{
|
||||
return g_pClientState;
|
||||
}
|
||||
|
||||
class VSplitScreen : public IDetour
|
||||
{
|
||||
virtual void GetAdr(void) const
|
||||
|
@ -126,7 +126,7 @@ public:
|
||||
int dword18CD0;
|
||||
int field_18CD4;
|
||||
float m_flFrameTime;
|
||||
int outgoing_command;
|
||||
int m_nOutgoingCommandNr;
|
||||
int current_movement_sequence_number;
|
||||
char gap18CE4[4];
|
||||
__int64 qword18CE8;
|
||||
|
@ -327,6 +327,15 @@ bool CNetChan::SendNetMsg(INetMessage& msg, bool bForceReliable, bool bVoice)
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: increments choked packet count
|
||||
//-----------------------------------------------------------------------------
|
||||
void CNetChan::SetChoked(void)
|
||||
{
|
||||
m_nOutSequenceNr++; // Sends to be done since move command use sequence number.
|
||||
m_nChokedPackets++;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Purpose: sets the remote frame times
|
||||
// Input : flFrameTime -
|
||||
|
@ -133,6 +133,7 @@ public:
|
||||
static void _Shutdown(CNetChan* pChan, const char* szReason, uint8_t bBadRep, bool bRemoveNow);
|
||||
static bool _ProcessMessages(CNetChan* pChan, bf_read* pMsg);
|
||||
|
||||
void SetChoked();
|
||||
void SetRemoteFramerate(float flFrameTime, float flFrameTimeStdDeviation);
|
||||
void SetRemoteCPUStatistics(uint8_t nStats);
|
||||
//-----------------------------------------------------------------------------
|
||||
|
@ -51,6 +51,13 @@
|
||||
0x1477876: 'FIELD_INTERVALFIELD_MODELINDEX\x00\x00' --> 'FIELD_INTERVAL\x00FIELD_MODELINDEX\x00';
|
||||
0x1318C00: 0x0000000000 --> 0x7792474101; // Add new entry in 'g_FieldTypes', this entry points to the 'FIELD_MODELINDEX' string we separated from 'FIELD_INTERVAL'.
|
||||
|
||||
// In 'CInput::JoyStickApplyMovement' an extraneous 'fmin' clamp is performed on the frame time. BinDiff revealed that this was no longer performed on
|
||||
// the 'Season 9.1 Genesis' executable. Further testing revealed that patching out just this clamp fixes the controller view stick problems when usercmd's
|
||||
// get dropped in CL_Move.
|
||||
0x6FD0E1: "movaps xmm0, xmm6" // Move frame time directly into the register that originally contained the clamped frame time.
|
||||
0x6FD0EE: "nop (x4)" // Nop 'minss xmm0, xmm6' (extraneous clamp).
|
||||
0x6FD114: "nop (x3)" // Nop 'movaps xmm6, xmm0' (extraneous move operation).
|
||||
|
||||
/////////////////////////////
|
||||
/////////////////////////////
|
||||
//// Exploitable defects ////
|
||||
|
Loading…
x
Reference in New Issue
Block a user