Подписывайтесь на наш Telegram и не пропускайте важные новости! Перейти

Исходник [Сурс] Valorant — Отрисовка иконок агентов через движок (UE4)

Sloppy
Начинающий
Начинающий
Статус
Оффлайн
Регистрация
13 Фев 2026
Сообщения
658
Реакции
18
Надоело таскать за собой огромные хедеры с байтами иконок?

Обычно народ не парится и просто пихает в сурс здоровенные массивы данных для каждой иконки агента. Это жирно, неудобно и вообще моветон для нормальных читов. Есть более изящный способ — запрячь сам Unreal Engine.

Логика простая: дергаем GetCharacterIcon через ProcessEvent, получаем объект текстуры и выводим его на канвас. Метод нативный, так что выглядит все аккуратно и не раздувает бинарник.

Код:
Expand Collapse Copy
            if (globals::visuals::esp_agent_icon)
            {
                const uintptr_t iconObj = AgentIconEsp::TryGetCharacterIconUObject(enemyPawn);
                if (iconObj)
                    (void)AgentIconEsp::DrawOnCanvas(k2Canvas, iconObj, rx, ry);
            }

Как это работает в коде:

Для начала вытягиваем саму функцию из движка и получаем UObject иконки:
Код:
Expand Collapse Copy
uintptr_t TryGetCharacterIconUObject(uintptr_t shooterPawn)
{
    if (!shooterPawn)
    {
        LogThrottled("agenticon_pawn", 4500, "[AgentIconEsp] DEBUG: TryGet pawn=NULL\n");
        return 0;
    }
 
    if (!g_GetCharacterIconFn)
        g_GetCharacterIconFn = uobject::find_object(L"ShooterGame.ShooterCharacter.GetCharacterIcon");
 
    if (!g_GetCharacterIconFn)
    {
        LogThrottled("agenticon_fn2", 5000,
            "[AgentIconEsp] DEBUG: find_object(GetCharacterIcon) -> NULL (nao ha UFunction para PE)\n");
        return 0;
    }
 
    alignas(16) unsigned char paramBlob[64]{};
    std::memset(paramBlob, 0, sizeof(paramBlob));
 
    reinterpret_cast<UObject*>(shooterPawn)->process_event(g_GetCharacterIconFn, paramBlob);
 
    const uintptr_t tex = *reinterpret_cast<uintptr_t*>(paramBlob);
    static DWORD s_peLog = 0;
    const DWORD t = GetTickCount();
    if (t - s_peLog > 3200)
    {
        s_peLog = t;
        const uintptr_t base = Core::GetGameModuleBase();
        const bool vtableOk = tex && base && Core::TestRead(tex, sizeof(void*));
        Logger::Log(
            "[AgentIconEsp] DEBUG: PE GetCharacterIcon pawn=%p fn=%p -> Out=%p (readable vtable guess=%d)\n",
            (void*)shooterPawn,
            (void*)g_GetCharacterIconFn,
            (void*)tex,
            vtableOk ? 1 : 0);
    }
 
    if (!tex)
        LogThrottled("agenticon_out", 4000, "[AgentIconEsp] DEBUG: PE retornou Out=NULL (personagem sem icone / timing)\n");
 
    return tex;
}

Когда текстура у нас на руках, отрисовываем её через K2_DrawTexture. Это требует наличия актуальных K2 оффсетов в вашем SDK.

Код:
Expand Collapse Copy
bool DrawOnCanvas(void* canvas, uintptr_t textureUObject, float rootScreenX, float rootScreenY)
{
    if (!canvas || !textureUObject)
    {
        LogThrottled("agenticon_draw_arg", 5000,
            "[AgentIconEsp] DEBUG: DrawOnCanvas canvas=%p tex=%p (skip)\n", canvas, (void*)textureUObject);
        return false;
    }
 
    const FLinearColor white{ 1.f, 1.f, 1.f, 1.f };
    const FVector2D pos{ rootScreenX - 10.f, rootScreenY + 28.f };
    const bool ok = CanvasDraw::K2_DrawTexture(canvas, reinterpret_cast<void*>(textureUObject), pos, FVector2D{ 20.f, 20.f },
        FVector2D{ 0.f, 0.f }, FVector2D{ 1.f, 1.f }, white, EBlendMode::Opaque, 0.f, FVector2D{ 0.5f, 0.5f });
 
    static DWORD s_k2Fail = 0;
    if (!ok)
    {
        const DWORD t = GetTickCount();
        if (t - s_k2Fail > 4500)
        {
            s_k2Fail = t;
            Logger::Log("[AgentIconEsp] DEBUG: K2_DrawTexture falhou (CanvasDraw resolvido=%d) tex=%p pos=(%.1f,%.1f)\n",
                CanvasDraw::IsK2DrawTextureResolved() ? 1 : 0,
                (void*)textureUObject,
                (double)pos.X,
                (double)pos.Y);
        }
    }
 
    return ok;
}

Технические нюансы:
  1. Для стабильной работы необходим корректный вызов ProcessEvent. Если ваш интернал криво хукает события, могут быть фризы.
  2. Метод работает только если вы имеете доступ к инстансу персонажа (shooterPawn) и самому канвасу.
  3. Это гораздо чище, чем хранить PNG в ресурсах или байт-массивах, так как вы юзаете то, что уже загружено в память игры.

В плане детекта Vanguard обычно смотрит на аномальные вызовы функций и левые потоки. Если вызовете это из нативного хука рендера самого движка, проблем быть не должно. Главное — следите за актуальностью оффсетов, при обновах сигнатуры функций GetCharacterIcon могут меняться.

Юзайте на здоровье, если лень возиться с внедрением сторонних ассетов под ESP.
 
Метод впечатляет, но не заметил — не влияет ли частый вызов ProcessEvent на производительность? У меня в похожих случаях хуки движка иногда провоцируют микрофризы. Может, есть нюансы в оптимизации, которые стоит учесть?
 
Назад
Сверху Снизу