diff --git a/src/client/component/dedicated.cpp b/src/client/component/dedicated.cpp index 3f5ea34f..ad437bbd 100644 --- a/src/client/component/dedicated.cpp +++ b/src/client/component/dedicated.cpp @@ -83,7 +83,11 @@ namespace dedicated // R_SyncGpu utils::hook::invoke(0x140E08AE0, a1); - std::this_thread::sleep_for(1ms); + const auto frame_time = *game::com_frameTime; + const auto sys_msec = game::Sys_Milliseconds(); + const auto msec = frame_time - sys_msec; + + std::this_thread::sleep_for(std::chrono::milliseconds(msec)); } void gscr_is_using_match_rules_data_stub() diff --git a/src/client/component/patches.cpp b/src/client/component/patches.cpp index 3b60244a..ce0baad8 100644 --- a/src/client/component/patches.cpp +++ b/src/client/component/patches.cpp @@ -40,7 +40,15 @@ namespace patches game::dvar_t* com_maxfps; name_dvar = game::Dvar_RegisterString("name", get_login_username().data(), game::DVAR_FLAG_SAVED, "Player name."); - com_maxfps = game::Dvar_RegisterInt("com_maxfps", 0, 0, 1000, game::DVAR_FLAG_SAVED, "Cap frames per second"); + + if (game::environment::is_dedi()) + { + com_maxfps = game::Dvar_RegisterInt("com_maxfps", 85, 0, 100, game::DVAR_FLAG_NONE, "Cap frames per second"); + } + else + { + com_maxfps = game::Dvar_RegisterInt("com_maxfps", 0, 0, 1000, game::DVAR_FLAG_SAVED, "Cap frames per second"); + } *reinterpret_cast(0x146005758) = com_maxfps; dvars::disable::re_register("com_maxfps"); @@ -222,7 +230,7 @@ namespace patches com_register_common_dvars_hook.create(0x140BADF30, com_register_common_dvars_stub); // patch some features - com_game_mode_supports_feature_hook.create(0x1405AFDE0, com_game_mode_supports_feature_stub); + com_game_mode_supports_feature_hook.create(game::Com_GameMode_SupportsFeature, com_game_mode_supports_feature_stub); // get client name from dvar utils::hook::jump(0x140D32770, live_get_local_client_name); diff --git a/src/client/component/timescale.cpp b/src/client/component/timescale.cpp new file mode 100644 index 00000000..50c83148 --- /dev/null +++ b/src/client/component/timescale.cpp @@ -0,0 +1,89 @@ +#include +#include "loader/component_loader.hpp" + +#include "game/game.hpp" +#include "game/dvars.hpp" + +#include "dvars.hpp" + +#include "gsc/script_extension.hpp" + +#include + +namespace timescale +{ + namespace + { + template + T get_timescale_safe(const scripting::value_wrap& arg) + { + if (arg.is()) + { + return arg.as(); + } + + return static_cast(arg.as()); + } + + void com_init_dobj_stub() + { + utils::hook::invoke(0x140BB17E0); // call original + + if (game::Com_GameMode_SupportsFeature(game::Com_GameMode_Feature::FEATURE_TIMESCALE)) + { + auto com_timescale = game::Dvar_FindVar("com_timescale"); + game::Dvar_SetFloat(com_timescale, 1.0f); + } + } + + utils::hook::detour com_set_slow_motion_hook; + void com_set_slow_motion_stub(float startTimescale, float endTimescale, int deltaMsec) + { + com_set_slow_motion_hook.invoke(startTimescale, endTimescale, deltaMsec); + + if (game::Com_GameMode_SupportsFeature(game::Com_GameMode_Feature::FEATURE_TIMESCALE)) + { + if (endTimescale == startTimescale) + { + auto com_timescale = game::Dvar_FindVar("com_timescale"); + game::Dvar_SetFloat(com_timescale, endTimescale); + } + } + } + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + dvars::override::register_float("timescale", 1.0f, 0.1f, 1000.0f, game::DVAR_FLAG_CHEAT); + dvars::override::register_float("com_timescale", 1.0f, 0.1f, 1000.0f, game::DVAR_FLAG_CHEAT | 0x40 | game::DVAR_FLAG_READ); + + // reset com_timeScale dvar in Com_Restart + utils::hook::call(0x140BAF16D, com_init_dobj_stub); + + // set com_timeScale in Com_SetSlowMotion + com_set_slow_motion_hook.create(game::Com_SetSlowMotion, com_set_slow_motion_stub); + + gsc::function::add("setslowmotion", [](const gsc::function_args& args) + { + if (args.size() == 0) + { + return scripting::script_value{}; + } + + const auto start = get_timescale_safe(args[0]); + const auto end = (args.size() > 0 ? get_timescale_safe(args[1]) : 1.0f); + const auto duration = (args.size() > 1 ? get_timescale_safe(args[2]) : 1) * 1000; + + game::SV_SetConfigString(game::CS_TIMESCALE, utils::string::va("%i %i %g %g", *game::gameTime, duration, start, end)); + game::Com_SetSlowMotion(start, end, duration); + + return scripting::script_value{}; + }); + } + }; +} + +REGISTER_COMPONENT(timescale::component) \ No newline at end of file diff --git a/src/client/game/game.cpp b/src/client/game/game.cpp index fa4cf07a..63e23e5f 100644 --- a/src/client/game/game.cpp +++ b/src/client/game/game.cpp @@ -244,7 +244,7 @@ namespace game va_end(ap); const auto file = dvars::g_log->current.string; - const auto time = *game::level_time / 1000; + const auto time = *game::gameTime / 1000; utils::io::write_file(file, utils::string::va("%3i:%i%i %s", time / 60, diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 6fc01b81..dd50ca28 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -560,6 +560,11 @@ namespace game SAY_TELL = 0x2, }; + enum ConfigString : std::int32_t + { + CS_TIMESCALE = 3464, + }; + namespace entity { enum connstate_t : std::uint32_t diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index e2e59f69..578195f6 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -37,12 +37,15 @@ namespace game WEAK symbol Com_GameMode_SetDesiredGameMode{ 0x1405AFDA0 }; WEAK symbol Com_GameMode_GetActiveGameMode{ 0x1405AFD50 }; WEAK symbol Com_GameMode_SupportsMap{ 0x1405AFE10 }; + WEAK symbol Com_GameMode_SupportsFeature{ 0x1405AFDE0 }; WEAK symbol Com_IsAnyLocalServerStarting{ 0x140BAD9C0 }; WEAK symbol Com_IsAnyLocalServerRunning{ 0x140BAD9A0 }; WEAK symbol Com_SetLocalizedErrorMessage{ 0x140BAF300 }; + WEAK symbol Com_SetSlowMotion{ 0x140BAFD70 }; + WEAK symbol Com_SyncThreads{ 0x140BB02D0 }; WEAK symbol Com_Shutdown{ 0x140BAFEA0 }; @@ -121,6 +124,7 @@ namespace game WEAK symbol Dvar_Reset{ 0x140CEC490 }; WEAK symbol Dvar_GenerateChecksum{ 0x140CEA520 }; WEAK symbol Dvar_SetInt{ 0x140CED3D0 }; + WEAK symbol Dvar_SetFloat{ 0x140CECD90 }; WEAK symbol Dvar_OverrideCheatProtection{ 0x140CEB250 }; WEAK symbol<__int64(const char* qpath, char** buffer)> FS_ReadFile{ 0x140CDE200 }; @@ -265,6 +269,7 @@ namespace game WEAK symbol SV_Loaded{ 0x140C114C0 }; WEAK symbol SV_MapExists{ 0x140CDB620 }; WEAK symbol SV_GetPlayerstateForClientNum{ 0x140C123A0 }; + WEAK symbol SV_SetConfigString{ 0x140C11CD0 }; WEAK symbol SV_AddBot{ 0x140C4E340 }; WEAK symbol SV_BotIsBot{ 0x140C3BC90 }; @@ -350,7 +355,9 @@ namespace game WEAK symbol g_quitRequested{ 0x14779CD44 }; WEAK symbol gameEntityId{ 0x14665A124 }; - WEAK symbol level_time{ 0x143C986D8 }; + WEAK symbol gameTime{ 0x143C986D8 }; + + WEAK symbol com_frameTime{ 0x1460053C0 }; WEAK symbol s_frontEndScene_state{ 0x144BFF608 };