Skip to main content

Overview

The frame_stage_notify hook is called at various stages during each game frame, allowing you to execute code at specific points in the engine’s update cycle. This is essential for tasks like updating entity lists, managing animations, and handling network updates.
This hook is called at virtual table index 37 of the i_client_dll interface.

Hook Signature

void __fastcall frame_stage_notify_detour(
    void* ecx,
    void* edx,
    sdk::frame_stage stage
)

Parameters

ecx
void*
ECX register (fastcall convention) - pointer to the interface
edx
void*
EDX register (fastcall convention) - not used
stage
sdk::frame_stage
The current frame stage being processed

Frame Stages

The frame_stage enum defines different points in the frame processing cycle:
enum class frame_stage : int
{
    UNDEFINED = -1,
    START,
    NET_UPDATE_START,
    NET_UPDATE_POSTDATAUPDATE_START,
    NET_UPDATE_POSTDATAUPDATE_END,
    NET_UPDATE_END,
    RENDER_START,
    RENDER_END
};

Stage Descriptions

UNDEFINED
-1
Invalid or uninitialized stage
START
0
Beginning of the frame
NET_UPDATE_START
1
Network update cycle begins
NET_UPDATE_POSTDATAUPDATE_START
2
Post-data update phase starts
NET_UPDATE_POSTDATAUPDATE_END
3
Post-data update phase ends
NET_UPDATE_END
4
Network update cycle completes - most commonly used
RENDER_START
5
Rendering phase begins
RENDER_END
6
Rendering phase completes

When It’s Called

This hook is invoked multiple times per frame at different stages. The most commonly used stage is NET_UPDATE_END, which occurs after all network data has been processed but before rendering.

Common Use Cases

Entity List Updates

Update cached entity information after network data arrives:
case NET_UPDATE_END:
    g_entity_list.update();
    break;

Event Processing

Fire game events immediately without delay:
for (auto event = g_interfaces.client_state->events; event; event = event->next)
    event->fire_delay = 0.f;

g_interfaces.engine->fire_events();

Animation Updates

Update client-side animations for enemy players:
for (auto& player_info : g_entity_list.players) {
    auto player = g_interfaces.entity_list->get_client_entity<sdk::c_cs_player*>(
        player_info.m_index
    );
    
    if (!player_info.m_valid || !player || !player->is_enemy(g_ctx.local))
        continue;
    
    g_ctx.updating_animations = true;
    // Update animations
}

Disabling Interpolation

Disable interpolation for more accurate entity positions:
auto var_mapping = player->get_var_map();

for (int iterator = 0; iterator < var_mapping->interpolated_entries; iterator++)
    var_mapping->entries[iterator].needs_to_interpolate = false;

Sound ESP

Track player positions based on sound emissions:
sdk::datatypes::c_utl_vector<sdk::sound_info> sounds{};
g_interfaces.engine_sound->get_active_sounds(sounds);

for (int iterator = 0; iterator < sounds.count(); iterator++) {
    auto& info = sounds[iterator];
    auto player = g_interfaces.entity_list->get_client_entity<sdk::c_cs_player*>(
        info.sound_source
    );
    
    // Trace sound to ground and store position
    sdk::ray_t ray;
    sdk::c_game_trace trace;
    ray.init(sound_origin, sound_floor);
    g_interfaces.engine_trace->trace_ray(ray, sdk::masks::MASK_ALL, &filter, &trace);
    
    g_entity_list.players[player->entity_index()].m_dormant_info.m_last_position = 
        trace.end_pos;
}
Always call the original function to ensure the game continues to process frames correctly:
hooks::frame_stage_notify_hook.call_original<void>(ecx, edx, stage);

Implementation Example

CREATE_HOOK_HELPER(
    frame_stage_notify_hook,
    void(__fastcall)(void*, void*, sdk::frame_stage)
);

static void __fastcall frame_stage_notify_detour(
    void* ecx,
    void* edx,
    sdk::frame_stage stage
);

Source Files

  • Header: globals/hooks/frame_stage_notify/frame_stage_notify.h:21-34
  • Implementation: globals/hooks/frame_stage_notify/frame_stage_notify.cpp:3-77
  • Enum: game/sdk/enums/frame_stage.h:5-16

Initialization

void init()
{
    hooks::frame_stage_notify_hook.create(
        virtual_func::get(g_interfaces.client, 37),
        frame_stage_notify_detour,
        _("frame_stage_notify_detour")
    );
}

See Also

Build docs developers (and LLMs) love