Skip to content

Commit

Permalink
Engine tick hook: Fix subtle stack corruption bug
Browse files Browse the repository at this point in the history
For some reason, the lambda was not preserving/setting up the
stack in the same way as a normal function does.
  • Loading branch information
praydog committed Feb 20, 2025
1 parent 7a0a148 commit e4d6b2e
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 53 deletions.
108 changes: 55 additions & 53 deletions src/mods/vr/FFakeStereoRenderingHook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -341,78 +341,80 @@ void FFakeStereoRenderingHook::attempt_hook_game_engine_tick(uintptr_t return_ad
}

// TODO: move this to a better place
m_tick_hook = safetyhook::create_inline((void*)*func, +[](sdk::UGameEngine* engine, float delta, bool idle) -> void* {
ZoneScopedN("UGameEngine::Tick Hook");
FrameMarkStart("UGameEngine::Tick");
m_tick_hook = safetyhook::create_inline((void*)*func, &engine_tick_hook, safetyhook::InlineHook::StartDisabled);

auto hook = g_hook;

hook->m_in_engine_tick = true;
if (!m_tick_hook) {
SPDLOG_ERROR("Failed to hook UGameEngine::Tick!");
return;
}

utility::ScopeGuard _{[]() {
g_hook->m_in_engine_tick = false;
FrameMarkEnd("UGameEngine::Tick");
}};

static bool once = true;
if (auto tick_hook_enable = m_tick_hook.enable(); !tick_hook_enable.has_value()) {
SPDLOG_ERROR("Failed to enable UGameEngine::Tick hook! {}", (int)tick_hook_enable.error().type);
return;
}

if (once) {
SPDLOG_INFO("First time calling UGameEngine::Tick!");
once = false;
}
m_hooked_game_engine_tick = true;

if (!g_framework->is_game_data_intialized()) {
return hook->m_tick_hook.unsafe_call<void*>(engine, delta, idle);
}
SPDLOG_INFO("Hooked UGameEngine::Tick!");
}

hook->attempt_hooking();
void* FFakeStereoRenderingHook::engine_tick_hook(sdk::UGameEngine* engine, float delta, bool idle) {
ZoneScopedN("UGameEngine::Tick Hook");
FrameMarkStart("UGameEngine::Tick");

// Best place to run game thread jobs.
GameThreadWorker::get().execute();
auto hook = g_hook;

hook->m_in_engine_tick = true;

if (hook->m_ignore_next_engine_tick) {
hook->m_ignored_engine_delta = delta;
hook->m_ignore_next_engine_tick = false;
return nullptr;
}

g_framework->enable_engine_thread();
g_framework->run_imgui_frame(false);
utility::ScopeGuard _{[]() {
g_hook->m_in_engine_tick = false;
FrameMarkEnd("UGameEngine::Tick");
}};

static bool once = true;

delta += hook->m_ignored_engine_delta;
hook->m_ignored_engine_delta = 0.0f;
if (once) {
SPDLOG_INFO("First time calling UGameEngine::Tick!");
once = false;
}

if (hook->m_tracking_system_hook != nullptr) {
hook->m_tracking_system_hook->on_pre_engine_tick(engine, delta);
}
if (!g_framework->is_game_data_intialized()) {
return hook->m_tick_hook.unsafe_call<void*>(engine, delta, idle);
}

const auto& mods = g_framework->get_mods()->get_mods();
for (auto& mod : mods) {
mod->on_pre_engine_tick(engine, delta);
}
hook->attempt_hooking();

const auto result = hook->m_tick_hook.unsafe_call<void*>(engine, delta, idle);
// Best place to run game thread jobs.
GameThreadWorker::get().execute();

for (auto& mod : mods) {
mod->on_post_engine_tick(engine, delta);
}
if (hook->m_ignore_next_engine_tick) {
hook->m_ignored_engine_delta = delta;
hook->m_ignore_next_engine_tick = false;
return nullptr;
}

g_framework->enable_engine_thread();
g_framework->run_imgui_frame(false);

return result;
}, safetyhook::InlineHook::StartDisabled);
delta += hook->m_ignored_engine_delta;
hook->m_ignored_engine_delta = 0.0f;

if (!m_tick_hook) {
SPDLOG_ERROR("Failed to hook UGameEngine::Tick!");
return;
if (hook->m_tracking_system_hook != nullptr) {
hook->m_tracking_system_hook->on_pre_engine_tick(engine, delta);
}

if (auto tick_hook_enable = m_tick_hook.enable(); !tick_hook_enable.has_value()) {
SPDLOG_ERROR("Failed to enable UGameEngine::Tick hook! {}", (int)tick_hook_enable.error().type);
return;
const auto& mods = g_framework->get_mods()->get_mods();
for (auto& mod : mods) {
mod->on_pre_engine_tick(engine, delta);
}

m_hooked_game_engine_tick = true;
const auto result = hook->m_tick_hook.unsafe_call<void*>(engine, delta, idle);

SPDLOG_INFO("Hooked UGameEngine::Tick!");
for (auto& mod : mods) {
mod->on_post_engine_tick(engine, delta);
}

return result;
}

namespace detail{
Expand Down
3 changes: 3 additions & 0 deletions src/mods/vr/FFakeStereoRenderingHook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,9 @@ class FFakeStereoRenderingHook : public ModComponent {
void post_init_properties(uintptr_t localplayer);

// Hooks
// UGameEngine
static void* engine_tick_hook(sdk::UGameEngine* engine, float delta, bool idle);

// FSceneView
static sdk::FSceneView* sceneview_constructor(sdk::FSceneView* sceneview, sdk::FSceneViewInitOptions* init_options, void* a3, void* a4);

Expand Down

0 comments on commit e4d6b2e

Please sign in to comment.