Add deterministic seed trigger (real random_seed spread simulation)
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
#include "CMisc.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <mmsystem.h>
|
||||
|
||||
@@ -30,6 +32,298 @@
|
||||
|
||||
#include <ImGui/imgui.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
// Deterministic Valve RNG (matches the game's math::random_seed generator).
|
||||
class CValveRng
|
||||
{
|
||||
public:
|
||||
auto Seed( int s ) -> void
|
||||
{
|
||||
m_State = -std::abs( s );
|
||||
m_Index = 0;
|
||||
m_bSeeded = false;
|
||||
}
|
||||
|
||||
auto Generate() -> int
|
||||
{
|
||||
if ( !m_bSeeded )
|
||||
{
|
||||
auto v = -m_State;
|
||||
|
||||
if ( v < 1 )
|
||||
v = 1;
|
||||
|
||||
for ( int j = 39; j >= 0; --j )
|
||||
{
|
||||
v = LCG( v );
|
||||
|
||||
if ( j < 32 )
|
||||
m_Table[ j ] = v;
|
||||
}
|
||||
|
||||
m_State = v;
|
||||
m_Index = m_Table[ 0 ];
|
||||
m_bSeeded = true;
|
||||
}
|
||||
|
||||
m_State = LCG( m_State );
|
||||
|
||||
const auto Index = m_Index / 0x4000000;
|
||||
m_Index = m_Table[ Index ];
|
||||
m_Table[ Index ] = m_State;
|
||||
|
||||
return m_Index;
|
||||
}
|
||||
|
||||
auto RandomFloat( float Min = 0.f , float Max = 1.f ) -> float
|
||||
{
|
||||
const auto Raw = Generate();
|
||||
const auto Norm = std::fminf( 0.99999988f , static_cast<float>( Raw ) * 4.6566129e-10f );
|
||||
|
||||
return Min + Norm * ( Max - Min );
|
||||
}
|
||||
|
||||
private:
|
||||
static auto LCG( int State ) -> int
|
||||
{
|
||||
const auto k = State / 127773;
|
||||
auto Result = 16807 * ( State - k * 127773 ) - 2836 * k;
|
||||
|
||||
if ( Result < 0 )
|
||||
Result += 2147483647;
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
int m_State = 0;
|
||||
int m_Index = 0;
|
||||
int m_Table[ 32 ]{};
|
||||
bool m_bSeeded = false;
|
||||
};
|
||||
|
||||
// Minimal SHA-1 (for computing the spread seed from angles + tick).
|
||||
class CSha1
|
||||
{
|
||||
public:
|
||||
auto Reset() -> void
|
||||
{
|
||||
m_State[ 0 ] = 0x67452301;
|
||||
m_State[ 1 ] = 0xEFCDAB89;
|
||||
m_State[ 2 ] = 0x98BADCFE;
|
||||
m_State[ 3 ] = 0x10325476;
|
||||
m_State[ 4 ] = 0xC3D2E1F0;
|
||||
m_Count = 0;
|
||||
}
|
||||
|
||||
auto Update( const void* pData , std::size_t Len ) -> void
|
||||
{
|
||||
const auto* pBytes = static_cast<const uint8_t*>( pData );
|
||||
auto Index = static_cast<std::size_t>( m_Count & 63 );
|
||||
m_Count += Len;
|
||||
|
||||
std::size_t i = 0;
|
||||
|
||||
if ( Index )
|
||||
{
|
||||
auto PartLen = 64 - Index;
|
||||
|
||||
if ( Len >= PartLen )
|
||||
{
|
||||
std::memcpy( m_Buffer + Index , pBytes , PartLen );
|
||||
Transform( m_Buffer );
|
||||
i = PartLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::memcpy( m_Buffer + Index , pBytes , Len );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for ( ; i + 64 <= Len; i += 64 )
|
||||
Transform( pBytes + i );
|
||||
|
||||
if ( i < Len )
|
||||
std::memcpy( m_Buffer , pBytes + i , Len - i );
|
||||
}
|
||||
|
||||
auto Final() -> void
|
||||
{
|
||||
uint8_t Padding[ 64 ]{};
|
||||
Padding[ 0 ] = 0x80;
|
||||
|
||||
const auto Index = static_cast<std::size_t>( m_Count & 63 );
|
||||
const auto PadLen = ( Index < 56 ) ? ( 56 - Index ) : ( 120 - Index );
|
||||
const auto BitCount = m_Count * 8;
|
||||
|
||||
Update( Padding , PadLen );
|
||||
|
||||
uint8_t Bits[ 8 ]{};
|
||||
|
||||
for ( int i = 0; i < 8; ++i )
|
||||
Bits[ 7 - i ] = static_cast<uint8_t>( BitCount >> ( i * 8 ) );
|
||||
|
||||
Update( Bits , 8 );
|
||||
|
||||
for ( int i = 0; i < 5; ++i )
|
||||
{
|
||||
m_Digest[ i * 4 + 0 ] = static_cast<uint8_t>( m_State[ i ] >> 24 );
|
||||
m_Digest[ i * 4 + 1 ] = static_cast<uint8_t>( m_State[ i ] >> 16 );
|
||||
m_Digest[ i * 4 + 2 ] = static_cast<uint8_t>( m_State[ i ] >> 8 );
|
||||
m_Digest[ i * 4 + 3 ] = static_cast<uint8_t>( m_State[ i ] );
|
||||
}
|
||||
}
|
||||
|
||||
auto GetFirstUInt32() const -> uint32_t
|
||||
{
|
||||
uint32_t Result;
|
||||
std::memcpy( &Result , m_Digest , sizeof Result );
|
||||
return Result;
|
||||
}
|
||||
|
||||
private:
|
||||
static auto RotL( uint32_t v , int n ) -> uint32_t
|
||||
{
|
||||
return ( v << n ) | ( v >> ( 32 - n ) );
|
||||
}
|
||||
|
||||
auto Transform( const uint8_t* pBlock ) -> void
|
||||
{
|
||||
uint32_t w[ 80 ]{};
|
||||
|
||||
for ( int i = 0; i < 16; ++i )
|
||||
w[ i ] = static_cast<uint32_t>( pBlock[ i * 4 ] ) << 24 | static_cast<uint32_t>( pBlock[ i * 4 + 1 ] ) << 16 | static_cast<uint32_t>( pBlock[ i * 4 + 2 ] ) << 8 | static_cast<uint32_t>( pBlock[ i * 4 + 3 ] );
|
||||
|
||||
for ( int i = 16; i < 80; ++i )
|
||||
w[ i ] = RotL( w[ i - 3 ] ^ w[ i - 8 ] ^ w[ i - 14 ] ^ w[ i - 16 ] , 1 );
|
||||
|
||||
auto a = m_State[ 0 ];
|
||||
auto b = m_State[ 1 ];
|
||||
auto c = m_State[ 2 ];
|
||||
auto d = m_State[ 3 ];
|
||||
auto e = m_State[ 4 ];
|
||||
|
||||
for ( int i = 0; i < 80; ++i )
|
||||
{
|
||||
uint32_t f , k;
|
||||
|
||||
if ( i < 20 )
|
||||
{
|
||||
f = ( b & c ) | ( ( ~b ) & d );
|
||||
k = 0x5A827999;
|
||||
}
|
||||
else if ( i < 40 )
|
||||
{
|
||||
f = b ^ c ^ d;
|
||||
k = 0x6ED9EBA1;
|
||||
}
|
||||
else if ( i < 60 )
|
||||
{
|
||||
f = ( b & c ) | ( b & d ) | ( c & d );
|
||||
k = 0x8F1BBCDC;
|
||||
}
|
||||
else
|
||||
{
|
||||
f = b ^ c ^ d;
|
||||
k = 0xCA62C1D6;
|
||||
}
|
||||
|
||||
const auto Temp = RotL( a , 5 ) + f + e + k + w[ i ];
|
||||
e = d;
|
||||
d = c;
|
||||
c = RotL( b , 30 );
|
||||
b = a;
|
||||
a = Temp;
|
||||
}
|
||||
|
||||
m_State[ 0 ] += a;
|
||||
m_State[ 1 ] += b;
|
||||
m_State[ 2 ] += c;
|
||||
m_State[ 3 ] += d;
|
||||
m_State[ 4 ] += e;
|
||||
}
|
||||
|
||||
uint32_t m_State[ 5 ]{};
|
||||
std::uint64_t m_Count = 0;
|
||||
uint8_t m_Buffer[ 64 ]{};
|
||||
uint8_t m_Digest[ 20 ]{};
|
||||
};
|
||||
|
||||
auto NormalizeAngle( float Angle ) -> float
|
||||
{
|
||||
return Angle - std::floorf( Angle * 0.0027777778f + 0.5f ) * 360.0f;
|
||||
}
|
||||
|
||||
auto QuantizeAngle( float Angle ) -> float
|
||||
{
|
||||
return std::floorf( NormalizeAngle( Angle ) * 2.0f ) * 0.5f;
|
||||
}
|
||||
|
||||
auto GetSpreadSeed( const QAngle& ViewAngles , int Tick ) -> uint32_t
|
||||
{
|
||||
struct
|
||||
{
|
||||
float Pitch;
|
||||
float Yaw;
|
||||
int PlayerRenderTick;
|
||||
} Buffer{};
|
||||
|
||||
Buffer.Pitch = QuantizeAngle( ViewAngles.m_x );
|
||||
Buffer.Yaw = QuantizeAngle( ViewAngles.m_y );
|
||||
Buffer.PlayerRenderTick = Tick;
|
||||
|
||||
CSha1 Hash;
|
||||
Hash.Reset();
|
||||
Hash.Update( &Buffer , 12 );
|
||||
Hash.Final();
|
||||
|
||||
return Hash.GetFirstUInt32();
|
||||
}
|
||||
|
||||
struct SeedSpread2_t
|
||||
{
|
||||
float x = 0.f;
|
||||
float y = 0.f;
|
||||
};
|
||||
|
||||
auto CalculateSeedSpread( int Seed , float Inaccuracy , float Spread , int ItemDefIdx ) -> SeedSpread2_t
|
||||
{
|
||||
constexpr auto k_RevolverId = 64;
|
||||
constexpr auto k_NegevId = 28;
|
||||
constexpr auto k_TwoPi = 6.2831853f;
|
||||
|
||||
CValveRng Rng;
|
||||
Rng.Seed( Seed );
|
||||
|
||||
auto InacR = Rng.RandomFloat( 0.f , 1.f );
|
||||
const auto InacA = Rng.RandomFloat( 0.f , k_TwoPi );
|
||||
|
||||
if ( ItemDefIdx == k_RevolverId )
|
||||
InacR = 1.f - ( InacR * InacR );
|
||||
else if ( ItemDefIdx == k_NegevId )
|
||||
InacR = 1.f - ( InacR * InacR );
|
||||
|
||||
InacR *= Inaccuracy;
|
||||
|
||||
auto SprR = Rng.RandomFloat( 0.f , 1.f );
|
||||
const auto SprA = Rng.RandomFloat( 0.f , k_TwoPi );
|
||||
|
||||
if ( ItemDefIdx == k_RevolverId )
|
||||
SprR = 1.f - ( SprR * SprR );
|
||||
else if ( ItemDefIdx == k_NegevId )
|
||||
SprR = 1.f - ( SprR * SprR );
|
||||
|
||||
SprR *= Spread;
|
||||
|
||||
return
|
||||
{
|
||||
std::cosf( SprA ) * SprR + std::cosf( InacA ) * InacR,
|
||||
std::sinf( SprA ) * SprR + std::sinf( InacA ) * InacR
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static CMisc g_CMisc{};
|
||||
|
||||
auto CMisc::OnRender() -> void
|
||||
@@ -278,7 +572,19 @@ auto CMisc::RunTriggerbot( CCSGOInput* pInput , CUserCmd* pUserCmd ) -> void
|
||||
return;
|
||||
}
|
||||
|
||||
const auto Result = GetCL_Trace()->TraceToHitGroup( pInput );
|
||||
TriggerTraceResult_t Result;
|
||||
|
||||
if ( Settings::Triggerbot::SeedTrigger )
|
||||
{
|
||||
Vector3 vDirection;
|
||||
|
||||
if ( CalculateSeedDirection( pInput , pUserCmd , &vDirection ) )
|
||||
Result = GetCL_Trace()->TraceDirectionToHitGroup( &vDirection );
|
||||
}
|
||||
else
|
||||
{
|
||||
Result = GetCL_Trace()->TraceToHitGroup( pInput );
|
||||
}
|
||||
|
||||
auto* pTargetPawn = reinterpret_cast<C_CSPlayerPawn*>( Result.pHitEntity );
|
||||
|
||||
@@ -300,7 +606,7 @@ auto CMisc::RunTriggerbot( CCSGOInput* pInput , CUserCmd* pUserCmd ) -> void
|
||||
default: bTargetValid = true; break;
|
||||
}
|
||||
|
||||
if ( bTargetValid && Settings::Triggerbot::Hitchance > 0 )
|
||||
if ( bTargetValid && !Settings::Triggerbot::SeedTrigger && Settings::Triggerbot::Hitchance > 0 )
|
||||
{
|
||||
const QAngle ViewAngles = *CCSGOInput_GetViewAngles( pInput , 0 );
|
||||
|
||||
@@ -410,6 +716,50 @@ auto CMisc::CalculateHitchance( CCSGOInput* pInput , C_CSPlayerPawn* pTarget , c
|
||||
return static_cast<float>( Hits ) / static_cast<float>( Samples );
|
||||
}
|
||||
|
||||
auto CMisc::CalculateSeedDirection( CCSGOInput* pInput , CUserCmd* pUserCmd , Vector3* vOutDirection ) -> bool
|
||||
{
|
||||
if ( !pInput || !vOutDirection )
|
||||
return false;
|
||||
|
||||
auto* pWeapon = GetCL_Weapons()->GetLocalActiveWeapon();
|
||||
|
||||
if ( !pWeapon )
|
||||
return false;
|
||||
|
||||
const QAngle ViewAngles = *CCSGOInput_GetViewAngles( pInput , 0 );
|
||||
|
||||
int iSeed = 0;
|
||||
|
||||
if ( pUserCmd && pUserCmd->cmd.base().has_random_seed() )
|
||||
{
|
||||
iSeed = pUserCmd->cmd.base().random_seed();
|
||||
}
|
||||
else
|
||||
{
|
||||
int iTick = 0;
|
||||
|
||||
if ( pUserCmd && pUserCmd->cmd.base().has_client_tick() )
|
||||
iTick = pUserCmd->cmd.base().client_tick();
|
||||
|
||||
iSeed = static_cast<int>( GetSpreadSeed( ViewAngles , iTick ) );
|
||||
}
|
||||
|
||||
const float flInaccuracy = C_CSWeaponBaseGun_GetInaccuracy( pWeapon , nullptr , nullptr );
|
||||
const float flSpread = C_CSWeaponBaseGun_GetSpread( pWeapon );
|
||||
|
||||
const auto ItemDefIdx = GetCL_Weapons()->GetLocalWeaponDefinitionIndex();
|
||||
|
||||
const auto sv = CalculateSeedSpread( iSeed + 1 , flInaccuracy , flSpread , ItemDefIdx );
|
||||
|
||||
Vector3 vForward , vRight , vUp;
|
||||
|
||||
Math::AngleVectors( ViewAngles , vForward , vRight , vUp );
|
||||
|
||||
*vOutDirection = ( vForward + vRight * -sv.x + vUp * sv.y ).Normalized();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CMisc::OnFrameStageNotify( int FrameStage ) -> void
|
||||
{
|
||||
if ( FrameStage != 6 )
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
private:
|
||||
auto RunTriggerbot( CCSGOInput* pInput , CUserCmd* pUserCmd ) -> void;
|
||||
auto CalculateHitchance( CCSGOInput* pInput , C_CSPlayerPawn* pTarget , const QAngle& ViewAngles , const int Samples ) -> float;
|
||||
auto CalculateSeedDirection( CCSGOInput* pInput , CUserCmd* pUserCmd , Vector3* vOutDirection ) -> bool;
|
||||
|
||||
private:
|
||||
std::chrono::steady_clock::time_point m_HitTime{};
|
||||
|
||||
@@ -393,10 +393,15 @@ auto CAndromedaMenu::RenderMiscPanel() -> void
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
Checkbox( "Seed-Modus (deterministisch)" , Settings::Triggerbot::SeedTrigger );
|
||||
|
||||
if ( !Settings::Triggerbot::SeedTrigger )
|
||||
{
|
||||
SliderInt( "Hitchance (%)" , Settings::Triggerbot::Hitchance , 0 , 100 , Settings::Triggerbot::Hitchance > 0 ? "%d%%" : "%d (aus)" );
|
||||
|
||||
if ( Settings::Triggerbot::Hitchance > 0 )
|
||||
SliderInt( "Hitchance Samples" , Settings::Triggerbot::HitchanceSamples , 16 , 128 );
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace Settings
|
||||
inline auto DrawIndicator = true;
|
||||
inline auto Hitchance = 0; // percent, 0 = off
|
||||
inline auto HitchanceSamples = 64;
|
||||
inline auto SeedTrigger = false; // deterministic: only fire if the real bullet (current random_seed) would hit
|
||||
}
|
||||
namespace Colors
|
||||
{
|
||||
|
||||
@@ -120,6 +120,35 @@ auto CL_Trace::TraceToHitGroup( CCSGOInput* pInput ) -> TriggerTraceResult_t
|
||||
return Result;
|
||||
}
|
||||
|
||||
auto CL_Trace::TraceDirectionToHitGroup( const Vector3* vDirection ) -> TriggerTraceResult_t
|
||||
{
|
||||
TriggerTraceResult_t Result;
|
||||
|
||||
auto pLocalPlayerPawn = GetCL_Players()->GetLocalPlayerPawn();
|
||||
|
||||
if ( !pLocalPlayerPawn || !vDirection )
|
||||
return Result;
|
||||
|
||||
Ray_t Ray;
|
||||
CGameTrace GameTrace;
|
||||
CTraceFilter Filter( 0x1C1003 , pLocalPlayerPawn , 3 , 15 );
|
||||
|
||||
const auto vStart = GetCL_Players()->GetLocalEyeOrigin();
|
||||
|
||||
const auto vEnd = vStart + ( *vDirection * 8192.f );
|
||||
|
||||
if ( IGamePhysicsQuery_TraceShape( SDK::Pointers::CVPhys2World() , Ray , vStart , vEnd , &Filter , &GameTrace ) )
|
||||
{
|
||||
if ( GameTrace.pHitEntity && GameTrace.pHitBox )
|
||||
{
|
||||
Result.pHitEntity = GameTrace.pHitEntity;
|
||||
Result.nHitGroup = GameTrace.pHitBox->nGroupId;
|
||||
}
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
auto GetCL_Trace() -> CL_Trace*
|
||||
{
|
||||
return &g_CL_Trace;
|
||||
|
||||
@@ -23,6 +23,7 @@ public:
|
||||
auto TraceToBoneEntity( CCSGOInput* pInput , const QAngle* AngleCorrection , QAngle* ViewAngleCorrection ) -> std::pair<uint64_t , C_BaseEntity*>;
|
||||
auto TraceToEntityEndPos( const Vector3* vEnd ) -> C_BaseEntity*;
|
||||
auto TraceToHitGroup( CCSGOInput* pInput ) -> TriggerTraceResult_t;
|
||||
auto TraceDirectionToHitGroup( const Vector3* vDirection ) -> TriggerTraceResult_t;
|
||||
};
|
||||
|
||||
auto GetCL_Trace() -> CL_Trace*;
|
||||
|
||||
Reference in New Issue
Block a user