From ca5e3df9bebd75dbba244b763d2f670b9f143686 Mon Sep 17 00:00:00 2001 From: Jack Martin Date: Thu, 2 Apr 2026 14:08:13 -0500 Subject: [PATCH] timescale manipulation --- mio-modding-api/hooks.cpp | 56 +++++++++++++++++++++++++++++-- mio-modding-api/mio-modding-api.h | 5 +++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/mio-modding-api/hooks.cpp b/mio-modding-api/hooks.cpp index 27fd90a..9aa4ff2 100644 --- a/mio-modding-api/hooks.cpp +++ b/mio-modding-api/hooks.cpp @@ -4,8 +4,6 @@ #include #include - - namespace ModAPI { namespace Hooks { namespace Combat { @@ -31,10 +29,64 @@ namespace ModAPI { } } + namespace Time { + float g_timeScale = 1.0f; + uintptr_t rtlqpc_trampoline = NULL; + std::vector> ontick_hooks; + + NOINLINE BOOL __stdcall ResolveRtlQPCHook(PLARGE_INTEGER PerformanceCount) { + typedef BOOL(*func)(PLARGE_INTEGER); + func trampoline = (func)(rtlqpc_trampoline); + + BOOL result = trampoline(PerformanceCount); + + static bool initialized = false; + static LONGLONG lastRealCount = 0; + static double accumulatedCount = 0.0; + + if (!initialized) { + lastRealCount = PerformanceCount->QuadPart; + accumulatedCount = (double)PerformanceCount->QuadPart; + initialized = true; + return result; + } + + LONGLONG delta = PerformanceCount->QuadPart - lastRealCount; + accumulatedCount += (double)delta * g_timeScale; + lastRealCount = PerformanceCount->QuadPart; + + PerformanceCount->QuadPart = (LONGLONG)accumulatedCount; + + for (auto& hook : ontick_hooks) { + hook((float)delta * g_timeScale); + } + + return result; + } + + MODDING_API void SetTimeScale(float scale) { + g_timeScale = scale; + } + + MODDING_API float GetTimeScale() { + return g_timeScale; + } + + MODDING_API void RunOnTick(std::function callback) { + ontick_hooks.push_back(callback); + } + } + // Initialize and connect hooks MODDING_API void InitializeHooks() { static PLH::NatDetour enemyhit_hook_detour = PLH::NatDetour((uintptr_t)ModAPI::Addresses::g_HitEnemyAddress, (uintptr_t)Combat::ResolveOnHitEnemyHook, &Combat::hitenemy_trampoline); enemyhit_hook_detour.hook(); + + static PLH::NatDetour rtlqpc_detour = PLH::NatDetour( + (uintptr_t)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlQueryPerformanceCounter"), + (uintptr_t)Time::ResolveRtlQPCHook, + &Time::rtlqpc_trampoline); + rtlqpc_detour.hook(); } } } \ No newline at end of file diff --git a/mio-modding-api/mio-modding-api.h b/mio-modding-api/mio-modding-api.h index f014375..5a4bb13 100644 --- a/mio-modding-api/mio-modding-api.h +++ b/mio-modding-api/mio-modding-api.h @@ -178,6 +178,11 @@ namespace ModAPI { namespace Combat { MODDING_API void RunOnHitEnemy(std::function callback); } + namespace Time { + MODDING_API void SetTimeScale(float scale); + MODDING_API float GetTimeScale(); + MODDING_API void RunOnTick(std::function callback); + } MODDING_API void InitializeHooks(); } } \ No newline at end of file