GucciManeLaFlare
Member
- Joined
- Jul 30, 2023
- Messages
- 5
- Reaction score
- 4
Salutations, niggas.
It is I, GUWOP, once again, speaking to you live from the bando, here to help out some young niggas with their shizz.
In this trapping episode, we will implement basic thread-safe rendering to our p100 hack, and use it to draw a dot at our origin.
This will be later used for our ESP, which will be another episode.
First, here's how to get a player's position:
Aight, aight, but this shizz 3D, how do I put it on my screen?
Needn't worry, GUWOP got you.
Dayum! Aight, aight, but where do I use this?
We want to be in a game thread, so we can interact with players safely.
but... SHEEIT! how will I draw my D3D9 ESP then? I can't just do that kinda shit wherever I want!
well, GUWOP got you here too... Lets get basic drawing on then first.
We'll begin with the renderer code, then with implementing it.
If you want more methods for drawing, then I'm sure you can figure it out, or wait until the next GUWOP episode.
Aight, let's get some drawing in this biii then.
We first want to hook Present, and throw our shit right before we present to screen.
Lets get a device pointer first, as always, GUWOP got you.
Now, the hooks, and example code for drawing a rectangle at our origin.
The queue clearing should be moved at the start of the hook, ideally you'd encapsulate that part and give another class as a pointer to a lambda which provides a push drawables function, so it isn't used wrong. Appropriate changes will be provided in the next GUWOP episode.
Until next time,
GUWOP - SCOOCHIE!
It is I, GUWOP, once again, speaking to you live from the bando, here to help out some young niggas with their shizz.
In this trapping episode, we will implement basic thread-safe rendering to our p100 hack, and use it to draw a dot at our origin.
This will be later used for our ESP, which will be another episode.
First, here's how to get a player's position:
Code:
void CPlayer::GetOrigin(Vector3D* vTo) const
{
const DWORD* pLocalPlayer = (const DWORD*)this;
int v7 = pLocalPlayer[169]; // 0x68AFF
const float* v8 = *(const float**)(v7 + 20);
(*vTo)[0] = v8[12];
(*vTo)[1] = v8[13];
(*vTo)[2] = v8[14];
}
Aight, aight, but this shizz 3D, how do I put it on my screen?
Needn't worry, GUWOP got you.
Code:
static constexpr DWORD g_worldToScreen = 0x71C20;
static constexpr DWORD g_dx = 0x26EB60;
CData::CData()
{
// ...
m_pDx = *(CDx**)((DWORD)(m_hmSampDll)+g_dx);
// ...
}
void CData::WorldToScreen(const Vector3D* pSrc, Vector3D* pOut)
{
typedef void(__thiscall* WorldToScreenFn_t)(CDx*, const Vector3D*, Vector3D*);
static WorldToScreenFn_t fnWorldtoScreen = (WorldToScreenFn_t)((DWORD)(m_hmSampDll)+g_worldToScreen);
fnWorldtoScreen(m_pDx, pSrc, pOut);
}
Dayum! Aight, aight, but where do I use this?
We want to be in a game thread, so we can interact with players safely.
but... SHEEIT! how will I draw my D3D9 ESP then? I can't just do that kinda shit wherever I want!
well, GUWOP got you here too... Lets get basic drawing on then first.
We'll begin with the renderer code, then with implementing it.
Code:
#pragma once
class IDrawable
{
public:
virtual ~IDrawable() {}
virtual void Draw(IDirect3DDevice9*) = 0;
};
class CRectangle : public IDrawable
{
public:
CRectangle(const D3DRECT rect, D3DCOLOR color);
void Draw(IDirect3DDevice9*) override;
private:
D3DRECT m_rect;
D3DCOLOR m_color;
};
class CQueue
{
public:
CQueue();
std::shared_mutex& GetMutex();
void Push(IDrawable*);
void Clear();
inline decltype(auto) begin()
{
return m_drawables.begin();
}
inline decltype(auto) end()
{
return m_drawables.end();
}
private:
std::shared_mutex m_mutex;
std::deque<std::unique_ptr<IDrawable>> m_drawables;
};
class CDrawing
{
public:
CDrawing();
void PushDrawables(std::function<void(CDrawing*)>&&);
void DrawQueue(IDirect3DDevice9*);
CQueue& GetQueue();
private:
CQueue m_queue;
};
Code:
#include "pch.h"
#include "Draw.h"
CRectangle::CRectangle(const D3DRECT rect, D3DCOLOR color)
{
m_rect = rect;
m_color = color;
}
void CRectangle::Draw(IDirect3DDevice9* pDevice)
{
pDevice->Clear(1, &m_rect, D3DCLEAR_TARGET, m_color, 0.f, 0);
}
CQueue::CQueue()
{
}
std::shared_mutex& CQueue::GetMutex()
{
return m_mutex;
}
void CQueue::Push(IDrawable* pDrawable)
{
m_drawables.push_front(std::move(std::unique_ptr<IDrawable>(pDrawable)));
}
void CQueue::Clear()
{
m_drawables.clear();
}
CDrawing::CDrawing()
{
}
void CDrawing::PushDrawables(std::function<void(CDrawing*)>&& fn)
{
std::unique_lock _(m_queue.GetMutex());
m_queue.Clear();
fn(this);
}
void CDrawing::DrawQueue(IDirect3DDevice9* pDevice)
{
std::unique_lock _(m_queue.GetMutex());
for (auto&& pDrawable : m_queue)
pDrawable->Draw(pDevice);
}
CQueue& CDrawing::GetQueue()
{
return m_queue;
}
CDrawing g_drawing{};
If you want more methods for drawing, then I'm sure you can figure it out, or wait until the next GUWOP episode.
Aight, let's get some drawing in this biii then.
We first want to hook Present, and throw our shit right before we present to screen.
Lets get a device pointer first, as always, GUWOP got you.
Code:
IDirect3DDevice9* CContext::GetDevice() const
{
return *(IDirect3DDevice9**)((DWORD)(this) + 327);
}
static constexpr DWORD g_context = 0x26EB88;
CData::CData()
{
m_pContext = *(CContext**)((DWORD)(m_hmSampDll)+g_context);
}
Now, the hooks, and example code for drawing a rectangle at our origin.
Code:
typedef int(__cdecl* GtaSaEveryFrame_t)(int, int, int);
static GtaSaEveryFrame_t g_ogGtaSaEveryFrame = nullptr;
int __cdecl GtaSaEveryFrameHook(int a, int b, int c)
{
// drawing
CPlayer* pLocal = g_data.GetLocalPlayer();
if (pLocal)
{
// get origin
Vector3D vOrigin;
pLocal->GetOrigin(&vOrigin);
// get screen position
Vector3D vScreen;
g_data.WorldToScreen(&vOrigin, &vScreen);
// queue origin rectangle to renderer
g_drawing.PushDrawables([vScreen](CDrawing* pDrawing) {
CQueue& queue = pDrawing->GetQueue();
const D3DRECT rect
{
.x1 = (LONG)(vScreen[0]) - 2,
.y1 = (LONG)(vScreen[1]) - 2,
.x2 = (LONG)(vScreen[0]) + 2,
.y2 = (LONG)(vScreen[1]) + 2
};
queue.Push(new CRectangle(rect, D3DCOLOR_XRGB(255, 255, 0)));
});
}
Hook::Remove(GtaSaEveryFrameHook);
int r = g_ogGtaSaEveryFrame(a, b, c);
Hook::Reinstall(GtaSaEveryFrameHook);
return r;
}
typedef void(_stdcall* PresentFn_t)(IDirect3DDevice9* pDevice, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion);
static PresentFn_t g_ogPresent = nullptr;
static void __stdcall PresentHook(IDirect3DDevice9* pDevice, CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion)
{
// render accumulated queue
g_drawing.DrawQueue(pDevice);
Hook::Remove(PresentHook);
g_ogPresent(pDevice, pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
Hook::Reinstall(PresentHook);
}
static constexpr DWORD g_fnGtaSaEveryFrame = 0x7F99B0; // can't disassemble in IDA, try ghidra decompiler
// it's also stored inside "dword_C97B24 + 152" but
// going there after, the fp is null
void Hooks::Install()
{
Hook::Install((void*)((DWORD)(g_data.GetGtaExe()) + g_fnGtaSaEveryFrame), GtaSaEveryFrameHook, (void**)&g_ogGtaSaEveryFrame);
void* pPresent = (*(void***)(g_data.GetContext()->GetDevice()))[17];
Hook::Install(pPresent, PresentHook, (void**)&g_ogPresent);
}
The queue clearing should be moved at the start of the hook, ideally you'd encapsulate that part and give another class as a pointer to a lambda which provides a push drawables function, so it isn't used wrong. Appropriate changes will be provided in the next GUWOP episode.
Until next time,
GUWOP - SCOOCHIE!