• This post is more about how I used the TraceRay function for my aimbot, not so much about how I calculated angles.
  • TLDR I used TraceRay from IEngineTrace to check if enemies were visible before activating my aimbot. In detail, I found the interface via CreateInterface("EngineTraceClient004"), set up a ray from my eye to their head, and checked the trace result.


Aimbot

Simple, it aims for you!

For mine, I just grab the closest enemy and snap my view angle to their head.

void RunAimbot()
{
	Player* closestEnemy = GetClosestEnemy();

	if (closestEnemy)
		hack->localPlayer->AimAt(closestEnemy->GetBonePos(8)); // 8: head
}

Trigonemtry (briefly)

Just in case you are curious–nothing fancy, just high school trig.

Vector3 deltaVec = { target.x - myPos->x, target.y - myPos->y, target.z - myPos->z };
float deltaVecLength = sqrt(deltaVec.x * deltaVec.x + deltaVec.y * deltaVec.y + deltaVec.z * deltaVec.z);

float pitch = -asin(deltaVec.z / deltaVecLength) * (180 / PI);
float yaw = atan2(deltaVec.y, deltaVec.x) * (180 / PI);

I used asin for pitch and atan2 for yaw based on the traingles formed between me and the enemy. Initially, my pitch was off (it was pointing the wrong way), so I reverted it after trial and error.

After calculating the view angles, I smoothed them before applying to avoid snapping.

TraceRay

To check if there’s a clear line of sight to the enemy, I used TraceRay to fire a ray from my eye position to the enemy’s head.

If you take a peek at Valve’s IEngineTrace.h, TraceRay is part of the IEngineTrace abstract class.

//-----------------------------------------------------------------------------
// Interface the engine exposes to the game DLL
//-----------------------------------------------------------------------------
#define INTERFACEVERSION_ENGINETRACE_SERVER	"EngineTraceServer003"
#define INTERFACEVERSION_ENGINETRACE_CLIENT	"EngineTraceClient003"
abstract_class IEngineTrace
{
public:
	// Returns the contents mask + entity at a particular world-space position
	virtual int		GetPointContents( const Vector &vecAbsPosition, IHandleEntity** ppEntity = NULL ) = 0;
	
	// Get the point contents, but only test the specific entity. This works
	// on static props and brush models.
	//
	// If the entity isn't a static prop or a brush model, it returns CONTENTS_EMPTY and sets
	// bFailed to true if bFailed is non-null.
	virtual int		GetPointContents_Collideable( ICollideable *pCollide, const Vector &vecAbsPosition ) = 0;

	// Traces a ray against a particular entity
	virtual void	ClipRayToEntity( const Ray_t &ray, unsigned int fMask, IHandleEntity *pEnt, trace_t *pTrace ) = 0;

	// Traces a ray against a particular entity
	virtual void	ClipRayToCollideable( const Ray_t &ray, unsigned int fMask, ICollideable *pCollide, trace_t *pTrace ) = 0;

	// A version that simply accepts a ray (can work as a traceline or tracehull)
	virtual void	TraceRay( const Ray_t &ray, unsigned int fMask, ITraceFilter *pTraceFilter, trace_t *pTrace ) = 0;

So no need to reimplement anything, just grab the interface and call the method! Work is already done by the game.

So how do we get this interface?

CreateInterface

If you open engine.dll in IDA pro and check the Exports tab, you can easily find CreateInterface.

createInterface_IDA

With this in mind, I define a function pointer for CreateInterface, grab its address, and call it with the name “EngineTraceClient004” to get the engine trace interface I need.

typedef void* (__cdecl* tCreateInterface)(const char* name, int* pReturnCode);
tCreateInterface CreateInterface = (tCreateInterface)GetProcAddress((HMODULE)engine, "CreateInterface");
EngineTrace = (IEngineTrace*)GetInterface(CreateInterface, "EngineTraceClient004");

You might notice Valve’s source code uses “EngineTraceClient003”. I first used it, but it didn’t work. Turned out that because the source code is from 2013, it shows you an outdated interface name as Valve continuously updates version name of the interface when they update the internal layout over time. So you should just use the latest one that works, “EngineTraceClient004” in my case.

Drawing a TraceRay

To use IEngineTrace, we first need to define a few classes and member functions used in the arguments and return value of TraceRay. I pulled these directly from the source code and organized them into Objects/Traceobjects.h.

Ray_t

Once the interface was set up, everything else was simple. I just searched how to draw a traceline with IEngineTrace and followed the steps.

CGameTrace trace;
Ray_t ray;
CTraceFilter tracefilter;
tracefilter.pSkip = (void*)this->GetEnt();

ray.Init(eyepos, targetheadpos);
hack->EngineTrace->TraceRay(ray, MASK_SHOT | CONTENTS_GRATE, &tracefilter, &trace);

Then I checked whether the ray’s fraction (i.e. how far it traveled before hitting something) was close to 1 and whether the hit entity matched my target player.

return (trace.fraction > 0.97f && (Ent*)player->GetEnt() == (Ent*)trace.hit_entity);

Finishing thoughts

A couple of years ago, I remember getting stuck on the aimbot for quite a while. So, I’m honestly pretty surprised how straightforward it was this time around with all the solid guides out there now.

Credits

Hats off to GuidedHacking! Especially their traceline tutorial, super helpful for understanding how to work with TraceRay!