I needed a way to hook networked props/get offsets simply in the source engine, so I implemented
ghetto's/altimor's recursive solution in my netvars class.
netvars.h:
#pragma once
#include "main.h"
class CNetworkedVariableManager
{
public:
// stores all tables, and all props inside those
CNetworkedVariableManager( void );
// calls GetProp wrapper to get the absolute offset of the prop
int GetOffset( const char *tableName, const char *propName );
// calls GetProp wrapper to get prop and sets the proxy of the prop
bool HookProp( const char *tableName, const char *propName, RecvVarProxyFn function );
private:
// wrapper so we can use recursion without too much performance loss
int GetProp( const char *tableName, const char *propName, RecvProp **prop = 0 );
// uses recursion to return a the relative offset to the given prop and sets the prop param
int GetProp( RecvTable *recvTable, const char *propName, RecvProp **prop = 0 );
RecvTable *GetTable( const char *tableName );
std::vector<RecvTable*> m_tables;
};
extern CNetworkedVariableManager *g_NetworkedVariableManager;
netvars.cpp:
#include "main.h"
CNetworkedVariableManager *g_NetworkedVariableManager = 0;
CNetworkedVariableManager::CNetworkedVariableManager( void )
{
m_tables.clear();
ClientClass *clientClass = g_InterfaceManager->Client()->GetOriginalMethod<GetAllClasses_t>( INDEX_GETALLCLASSES )( g_InterfaceManager->Client()->thisptr() );
if ( !clientClass )
{
LOG_ERROR( "ClientClass was not found" );
return;
}
while ( clientClass )
{
RecvTable *recvTable = clientClass->m_pRecvTable;
m_tables.push_back( recvTable );
clientClass = clientClass->m_pNext;
}
}
// calls GetProp wrapper to get the absolute offset of the prop
int CNetworkedVariableManager::GetOffset( const char *tableName, const char *propName )
{
int offset = GetProp( tableName, propName );
if ( !offset )
{
LOG_ERROR( "Failed to find offset for prop: %s from table: %s", propName, tableName );
return 0;
}
return offset;
}
// calls GetProp wrapper to get prop and sets the proxy of the prop
bool CNetworkedVariableManager::HookProp( const char *tableName, const char *propName, RecvVarProxyFn function )
{
RecvProp *recvProp = 0;
GetProp( tableName, propName, &recvProp );
if ( !recvProp )
{
LOG_ERROR( "Failed to hook prop: %s from table: %s", propName, tableName );
return false;
}
recvProp->m_ProxyFn = function;
return true;
}
// wrapper so we can use recursion without too much performance loss
int CNetworkedVariableManager::GetProp( const char *tableName, const char *propName, RecvProp **prop )
{
RecvTable *recvTable = GetTable( tableName );
if ( !recvTable )
{
LOG_ERROR( "Failed to find table: %s", tableName );
return 0;
}
int offset = GetProp( recvTable, propName, prop );
if ( !offset )
{
LOG_ERROR( "Failed to find prop: %s from table: %s", propName, tableName );
return 0;
}
return offset;
}
// uses recursion to return a the relative offset to the given prop and sets the prop param
int CNetworkedVariableManager::GetProp( RecvTable *recvTable, const char *propName, RecvProp **prop )
{
int extraOffset = 0;
for ( int i = 0; i < recvTable->m_nProps; ++i )
{
RecvProp *recvProp = &recvTable->m_pProps[i];
RecvTable *child = recvProp->m_pDataTable;
if ( child
&& ( child->m_nProps > 0 ) )
{
int tmp = GetProp( child, propName, prop );
if ( tmp )
{
extraOffset += ( recvProp->m_Offset + tmp );
}
}
if ( stricmp( recvProp->m_pVarName, propName ) )
{
continue;
}
if ( prop )
{
*prop = recvProp;
}
return ( recvProp->m_Offset + extraOffset );
}
return extraOffset;
}
RecvTable *CNetworkedVariableManager::GetTable( const char *tableName )
{
if ( m_tables.empty() )
{
LOG_ERROR( "Failed to find table: %s (m_tables is empty)", tableName );
return 0;
}
for each ( RecvTable *table in m_tables )
{
if ( !table )
{
continue;
}
if ( stricmp( table->m_pNetTableName, tableName ) == 0 )
{
return table;
}
}
return 0;
}
example usages:
// we need to construct this manually
g_NetworkedVariableManager = new CNetworkedVariableManager();
// hook netvar proxy
g_NetworkedVariableManager->HookProp( "DT_CSPlayer", "m_angEyeAngles[0]", CSPlayer_EyeAnglesX );
Vector CPlayer::GetViewOffset( void )
{
static int offset = g_NetworkedVariableManager->GetOffset( "DT_BasePlayer", "m_vecViewOffset[0]" );
return *Member<Vector*>( this, offset );
}