From 7bf866ddf61d55c43fafed8a42b15278d4cc03b9 Mon Sep 17 00:00:00 2001 From: githubawn <115191165+githubawn@users.noreply.github.com> Date: Tue, 5 May 2026 23:40:48 +0200 Subject: [PATCH 1/6] chore(frame-pacer): overhaul logic and render fps limits and presets - Added 15 FPS to render presets and expanded logic presets (1 to 960 FPS). - Implemented array-based preset snapping for logic FPS. - Renamed extraStep to snapValue for clarity. - Removed redundant logic speed scaling guards and safety asserts. - Cleaned up stale comments and dead code in CommandXlat. --- .../Include/Common/FrameRateLimit.h | 18 ++---- .../Source/Common/FrameRateLimit.cpp | 63 +++++++++++++++---- .../GameClient/MessageStream/CommandXlat.cpp | 30 +++------ 3 files changed, 64 insertions(+), 47 deletions(-) diff --git a/Core/GameEngine/Include/Common/FrameRateLimit.h b/Core/GameEngine/Include/Common/FrameRateLimit.h index a729313d005..13d4a877d42 100644 --- a/Core/GameEngine/Include/Common/FrameRateLimit.h +++ b/Core/GameEngine/Include/Common/FrameRateLimit.h @@ -62,18 +62,12 @@ class RenderFpsPreset class LogicTimeScaleFpsPreset { public: - enum CPP_11(: UnsignedInt) - { -#if RTS_DEBUG - MinFpsValue = 5, -#else - MinFpsValue = LOGICFRAMES_PER_SECOND, -#endif - StepFpsValue = 5, - }; - static UnsignedInt getNextFpsValue(UnsignedInt value); - static UnsignedInt getPrevFpsValue(UnsignedInt value); - static UnsignedInt changeFpsValue(UnsignedInt value, FpsValueChange change); + static UnsignedInt getNextFpsValue(UnsignedInt value, UnsignedInt snapValue = 0); + static UnsignedInt getPrevFpsValue(UnsignedInt value, UnsignedInt snapValue = 0); + static UnsignedInt changeFpsValue(UnsignedInt value, FpsValueChange change, UnsignedInt snapValue = 0); + +private: + static const UnsignedInt s_fpsValues[]; }; diff --git a/Core/GameEngine/Source/Common/FrameRateLimit.cpp b/Core/GameEngine/Source/Common/FrameRateLimit.cpp index 940b44f88c4..349e4cd14a1 100644 --- a/Core/GameEngine/Source/Common/FrameRateLimit.cpp +++ b/Core/GameEngine/Source/Common/FrameRateLimit.cpp @@ -67,9 +67,10 @@ void FrameRateLimit::reset() const UnsignedInt RenderFpsPreset::s_fpsValues[] = { - 30, 50, 56, 60, 65, 70, 72, 75, 80, 85, 90, 100, 110, 120, 144, 240, 480, UncappedFpsValue }; + 15, 30, 50, 56, 60, 65, 70, 72, 75, 80, 85, 90, 100, 110, 120, 144, 240, 480, UncappedFpsValue }; -static_assert(LOGICFRAMES_PER_SECOND <= 30, "Min FPS values need to be revisited!"); +const UnsignedInt LogicTimeScaleFpsPreset::s_fpsValues[] = { + 1, 5, 15, 30, 45, 60, 75, 90, 105, 120, 240, 480, 960, RenderFpsPreset::UncappedFpsValue }; UnsignedInt RenderFpsPreset::getNextFpsValue(UnsignedInt value) { @@ -109,30 +110,66 @@ UnsignedInt RenderFpsPreset::changeFpsValue(UnsignedInt value, FpsValueChange ch } } - -UnsignedInt LogicTimeScaleFpsPreset::getNextFpsValue(UnsignedInt value) +UnsignedInt LogicTimeScaleFpsPreset::getNextFpsValue(UnsignedInt value, UnsignedInt snapValue) { - return value + StepFpsValue; + UnsignedInt nextValue = s_fpsValues[ARRAY_SIZE(s_fpsValues) - 1]; // Default to Uncapped + + // Check if snapValue (e.g. current render FPS) is the next closest candidate + if (snapValue > value && snapValue < nextValue) + { + nextValue = snapValue; + } + + // Check predefined steps + for (size_t i = 0; i < ARRAY_SIZE(s_fpsValues); ++i) + { + if (s_fpsValues[i] > value) + { + if (s_fpsValues[i] < nextValue) + { + nextValue = s_fpsValues[i]; + } + break; + } + } + + return nextValue; } -UnsignedInt LogicTimeScaleFpsPreset::getPrevFpsValue(UnsignedInt value) +UnsignedInt LogicTimeScaleFpsPreset::getPrevFpsValue(UnsignedInt value, UnsignedInt snapValue) { - if (value - StepFpsValue < MinFpsValue) + UnsignedInt prevValue = s_fpsValues[0]; // Floor/seed for the search loop + + // Check if snapValue (e.g. current render FPS) is the previous closest candidate. + // Note: if snapValue == value, neither branch below fires and the snap point is + // intentionally skipped — the caller must step to a different preset. + if (snapValue < value && snapValue > prevValue) { - return MinFpsValue; + prevValue = snapValue; } - else + + // Check predefined steps + for (int i = (int)ARRAY_SIZE(s_fpsValues) - 1; i >= 0; --i) { - return value - StepFpsValue; + if (s_fpsValues[i] < value) + { + if (s_fpsValues[i] > prevValue) + { + prevValue = s_fpsValues[i]; + } + break; + } } + + return prevValue; } -UnsignedInt LogicTimeScaleFpsPreset::changeFpsValue(UnsignedInt value, FpsValueChange change) +UnsignedInt LogicTimeScaleFpsPreset::changeFpsValue(UnsignedInt value, FpsValueChange change, UnsignedInt snapValue) { switch (change) { default: - case FpsValueChange_Increase: return getNextFpsValue(value); - case FpsValueChange_Decrease: return getPrevFpsValue(value); + case FpsValueChange_Increase: return getNextFpsValue(value, snapValue); + case FpsValueChange_Decrease: return getPrevFpsValue(value, snapValue); } } diff --git a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 7e322b28241..45621d6e665 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -210,38 +210,24 @@ bool changeLogicTimeScale(FpsValueChange change) return false; const UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit(); - UnsignedInt maxRenderRemainder = LogicTimeScaleFpsPreset::StepFpsValue; - maxRenderRemainder -= maxRenderFps % LogicTimeScaleFpsPreset::StepFpsValue; - maxRenderRemainder %= LogicTimeScaleFpsPreset::StepFpsValue; - UnsignedInt logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); - // Set the value to the max render fps value plus a bit when time scale is - // disabled. This ensures that the time scale does not re-enable with a - // 'surprise' value. + if (!TheFramePacer->isLogicTimeScaleEnabled()) { - logicTimeScaleFps = maxRenderFps + maxRenderRemainder; + logicTimeScaleFps = maxRenderFps; } - // Ceil the value at the max render fps value plus a bit so that the next fps - // value decrease would undercut the max render fps at the correct step value. - // Example: render fps 72 -> logic value ceiled to 75 -> decreased to 70. - logicTimeScaleFps = min(logicTimeScaleFps, maxRenderFps + maxRenderRemainder); - logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change); - // Set value before potentially disabling it. - if (TheFramePacer->isLogicTimeScaleEnabled()) + logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change, maxRenderFps); + + // Ensure logic FPS never exceeds render FPS + if (logicTimeScaleFps > maxRenderFps && logicTimeScaleFps != RenderFpsPreset::UncappedFpsValue) { - TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); + logicTimeScaleFps = maxRenderFps; } + TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); TheFramePacer->enableLogicTimeScale(logicTimeScaleFps < maxRenderFps); - // Set value after potentially enabling it. - if (TheFramePacer->isLogicTimeScaleEnabled()) - { - TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); - } - logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps(); const Real actualLogicTimeScaleRatio = TheFramePacer->getActualLogicTimeScaleRatio(); From 89f39b164052eee7647834bf262a899bc62076fa Mon Sep 17 00:00:00 2001 From: githubawn <115191165+githubawn@users.noreply.github.com> Date: Mon, 11 May 2026 19:19:07 +0200 Subject: [PATCH 2/6] greptile fixes --- Core/GameEngine/Source/Common/FrameRateLimit.cpp | 1 + .../Source/GameClient/MessageStream/CommandXlat.cpp | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Core/GameEngine/Source/Common/FrameRateLimit.cpp b/Core/GameEngine/Source/Common/FrameRateLimit.cpp index 349e4cd14a1..ba40a828a60 100644 --- a/Core/GameEngine/Source/Common/FrameRateLimit.cpp +++ b/Core/GameEngine/Source/Common/FrameRateLimit.cpp @@ -69,6 +69,7 @@ void FrameRateLimit::reset() const UnsignedInt RenderFpsPreset::s_fpsValues[] = { 15, 30, 50, 56, 60, 65, 70, 72, 75, 80, 85, 90, 100, 110, 120, 144, 240, 480, UncappedFpsValue }; +// TheSuperHackers @info s_fpsValues MUST be strictly ascending; the search loops break on first match. const UnsignedInt LogicTimeScaleFpsPreset::s_fpsValues[] = { 1, 5, 15, 30, 45, 60, 75, 90, 105, 120, 240, 480, 960, RenderFpsPreset::UncappedFpsValue }; diff --git a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 45621d6e665..1ecc6675066 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -225,8 +225,13 @@ bool changeLogicTimeScale(FpsValueChange change) logicTimeScaleFps = maxRenderFps; } - TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); - TheFramePacer->enableLogicTimeScale(logicTimeScaleFps < maxRenderFps); + const bool enableTimescale = (logicTimeScaleFps < maxRenderFps); + // TheSuperHackers @info Preserve the last real FPS in m_logicTimeScaleFPS so re-enabling timescale resumes from a sane value. + if (enableTimescale) + { + TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); + } + TheFramePacer->enableLogicTimeScale(enableTimescale); logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps(); From a03033fa297563167caae2d52c5ca4c37e9fd676 Mon Sep 17 00:00:00 2001 From: githubawn <115191165+githubawn@users.noreply.github.com> Date: Thu, 14 May 2026 17:52:55 +0200 Subject: [PATCH 3/6] implemented feedback --- .../Source/Common/FrameRateLimit.cpp | 30 ++++++++++++------- .../GameClient/MessageStream/CommandXlat.cpp | 3 +- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Core/GameEngine/Source/Common/FrameRateLimit.cpp b/Core/GameEngine/Source/Common/FrameRateLimit.cpp index ba40a828a60..840daa60802 100644 --- a/Core/GameEngine/Source/Common/FrameRateLimit.cpp +++ b/Core/GameEngine/Source/Common/FrameRateLimit.cpp @@ -113,7 +113,7 @@ UnsignedInt RenderFpsPreset::changeFpsValue(UnsignedInt value, FpsValueChange ch UnsignedInt LogicTimeScaleFpsPreset::getNextFpsValue(UnsignedInt value, UnsignedInt snapValue) { - UnsignedInt nextValue = s_fpsValues[ARRAY_SIZE(s_fpsValues) - 1]; // Default to Uncapped + UnsignedInt nextValue = s_fpsValues[ARRAY_SIZE(s_fpsValues) - 1]; // Defaults to Uncapped // Check if snapValue (e.g. current render FPS) is the next closest candidate if (snapValue > value && snapValue < nextValue) @@ -124,11 +124,12 @@ UnsignedInt LogicTimeScaleFpsPreset::getNextFpsValue(UnsignedInt value, Unsigned // Check predefined steps for (size_t i = 0; i < ARRAY_SIZE(s_fpsValues); ++i) { - if (s_fpsValues[i] > value) + const UnsignedInt fpsValue = s_fpsValues[i]; + if (fpsValue > value) { - if (s_fpsValues[i] < nextValue) + if (fpsValue < nextValue) { - nextValue = s_fpsValues[i]; + nextValue = fpsValue; } break; } @@ -144,7 +145,7 @@ UnsignedInt LogicTimeScaleFpsPreset::getPrevFpsValue(UnsignedInt value, Unsigned // Check if snapValue (e.g. current render FPS) is the previous closest candidate. // Note: if snapValue == value, neither branch below fires and the snap point is // intentionally skipped — the caller must step to a different preset. - if (snapValue < value && snapValue > prevValue) + if (snapValue > prevValue && snapValue < value) { prevValue = snapValue; } @@ -152,11 +153,12 @@ UnsignedInt LogicTimeScaleFpsPreset::getPrevFpsValue(UnsignedInt value, Unsigned // Check predefined steps for (int i = (int)ARRAY_SIZE(s_fpsValues) - 1; i >= 0; --i) { - if (s_fpsValues[i] < value) + const UnsignedInt fpsValue = s_fpsValues[i]; + if (fpsValue < value) { - if (s_fpsValues[i] > prevValue) + if (fpsValue > prevValue) { - prevValue = s_fpsValues[i]; + prevValue = fpsValue; } break; } @@ -169,8 +171,14 @@ UnsignedInt LogicTimeScaleFpsPreset::changeFpsValue(UnsignedInt value, FpsValueC { switch (change) { - default: - case FpsValueChange_Increase: return getNextFpsValue(value, snapValue); - case FpsValueChange_Decrease: return getPrevFpsValue(value, snapValue); + case FpsValueChange_Increase: + return getNextFpsValue(value, snapValue); + + case FpsValueChange_Decrease: + return getPrevFpsValue(value, snapValue); + + default: + assert(false); + return value; } } diff --git a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index 1ecc6675066..d9e56b808d2 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -226,13 +226,14 @@ bool changeLogicTimeScale(FpsValueChange change) } const bool enableTimescale = (logicTimeScaleFps < maxRenderFps); + // TheSuperHackers @info Preserve the last real FPS in m_logicTimeScaleFPS so re-enabling timescale resumes from a sane value. if (enableTimescale) { TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); } - TheFramePacer->enableLogicTimeScale(enableTimescale); + TheFramePacer->enableLogicTimeScale(enableTimescale); logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps(); const Real actualLogicTimeScaleRatio = TheFramePacer->getActualLogicTimeScaleRatio(); From 0e498dfb9c877c2ab92041610e61cca2c967ad07 Mon Sep 17 00:00:00 2001 From: githubawn <115191165+githubawn@users.noreply.github.com> Date: Fri, 31 Jul 2026 22:48:30 +0200 Subject: [PATCH 4/6] Fix review comments: simplify UncappedFpsValue and clamp multiplayer render FPS --- Core/GameEngine/Source/Common/FramePacer.cpp | 3 ++- Core/GameEngine/Source/Common/FrameRateLimit.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Core/GameEngine/Source/Common/FramePacer.cpp b/Core/GameEngine/Source/Common/FramePacer.cpp index 8f83bff10fb..a193c189141 100644 --- a/Core/GameEngine/Source/Common/FramePacer.cpp +++ b/Core/GameEngine/Source/Common/FramePacer.cpp @@ -111,7 +111,8 @@ Bool FramePacer::isActualFramesPerSecondLimitEnabled() const Int FramePacer::getActualFramesPerSecondLimit() const { - return isActualFramesPerSecondLimitEnabled() ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue; + const Int limit = isActualFramesPerSecondLimitEnabled() ? getFramesPerSecondLimit() : RenderFpsPreset::UncappedFpsValue; + return (TheNetwork != nullptr) ? std::max(limit, static_cast(LOGICFRAMES_PER_SECOND)) : limit; } Real FramePacer::getUpdateTime() const diff --git a/Core/GameEngine/Source/Common/FrameRateLimit.cpp b/Core/GameEngine/Source/Common/FrameRateLimit.cpp index 840daa60802..c625c831ca7 100644 --- a/Core/GameEngine/Source/Common/FrameRateLimit.cpp +++ b/Core/GameEngine/Source/Common/FrameRateLimit.cpp @@ -113,7 +113,7 @@ UnsignedInt RenderFpsPreset::changeFpsValue(UnsignedInt value, FpsValueChange ch UnsignedInt LogicTimeScaleFpsPreset::getNextFpsValue(UnsignedInt value, UnsignedInt snapValue) { - UnsignedInt nextValue = s_fpsValues[ARRAY_SIZE(s_fpsValues) - 1]; // Defaults to Uncapped + UnsignedInt nextValue = RenderFpsPreset::UncappedFpsValue; // Check if snapValue (e.g. current render FPS) is the next closest candidate if (snapValue > value && snapValue < nextValue) From 1c0ed8dca7cb2ea16c3ce65ab0d0e6affcf85915 Mon Sep 17 00:00:00 2001 From: githubawn <115191165+githubawn@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:04:02 +0200 Subject: [PATCH 5/6] consolidate functions --- Core/GameEngine/Include/Common/FramePacer.h | 3 +-- Core/GameEngine/Include/Common/FrameRateLimit.h | 10 ++++++---- Core/GameEngine/Source/Common/FramePacer.cpp | 11 +++++------ Core/GameEngine/Source/Common/FrameRateLimit.cpp | 1 + .../Source/GameClient/MessageStream/CommandXlat.cpp | 7 +------ 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/Core/GameEngine/Include/Common/FramePacer.h b/Core/GameEngine/Include/Common/FramePacer.h index c8681cab7a7..04462bb2232 100644 --- a/Core/GameEngine/Include/Common/FramePacer.h +++ b/Core/GameEngine/Include/Common/FramePacer.h @@ -57,9 +57,8 @@ class FramePacer Bool isTimeFrozen() const; Bool isGameHalted() const; - void setLogicTimeScaleFps( Int fps ); ///< Set the logic time scale fps and therefore scale the simulation time. Is capped by the max render fps and does not apply to network matches. Int getLogicTimeScaleFps() const; ///< Get the raw logic time scale fps value. - void enableLogicTimeScale( Bool enable ); ///< Enable or disable the logic time scale setup. If disabled, the simulation time scale is bound to the render frame time or network update time. + void enableLogicTimeScale( Bool enable, Int fps = -1 ); ///< Enable or disable the logic time scale setup. If fps >= 0, also sets the logic time scale fps before enabling. If disabled, the simulation time scale is bound to the render frame time or network update time. Bool isLogicTimeScaleEnabled() const; ///< Check whether the logic time scale setup is enabled. Int getActualLogicTimeScaleFps(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale fps, depending on the max render fps, network state and enabled state. Real getActualLogicTimeScaleRatio(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale ratio, depending on the max render fps, network state and enabled state. diff --git a/Core/GameEngine/Include/Common/FrameRateLimit.h b/Core/GameEngine/Include/Common/FrameRateLimit.h index 13d4a877d42..5274df301c1 100644 --- a/Core/GameEngine/Include/Common/FrameRateLimit.h +++ b/Core/GameEngine/Include/Common/FrameRateLimit.h @@ -50,11 +50,12 @@ class RenderFpsPreset UncappedFpsValue = 1000000, }; - static UnsignedInt getNextFpsValue(UnsignedInt value); - static UnsignedInt getPrevFpsValue(UnsignedInt value); static UnsignedInt changeFpsValue(UnsignedInt value, FpsValueChange change); private: + static UnsignedInt getNextFpsValue(UnsignedInt value); + static UnsignedInt getPrevFpsValue(UnsignedInt value); + static const UnsignedInt s_fpsValues[]; }; @@ -63,11 +64,12 @@ class LogicTimeScaleFpsPreset { public: - static UnsignedInt getNextFpsValue(UnsignedInt value, UnsignedInt snapValue = 0); - static UnsignedInt getPrevFpsValue(UnsignedInt value, UnsignedInt snapValue = 0); static UnsignedInt changeFpsValue(UnsignedInt value, FpsValueChange change, UnsignedInt snapValue = 0); private: + static UnsignedInt getNextFpsValue(UnsignedInt value, UnsignedInt snapValue = 0); + static UnsignedInt getPrevFpsValue(UnsignedInt value, UnsignedInt snapValue = 0); + static const UnsignedInt s_fpsValues[]; }; diff --git a/Core/GameEngine/Source/Common/FramePacer.cpp b/Core/GameEngine/Source/Common/FramePacer.cpp index a193c189141..ffb36179c25 100644 --- a/Core/GameEngine/Source/Common/FramePacer.cpp +++ b/Core/GameEngine/Source/Common/FramePacer.cpp @@ -152,18 +152,17 @@ Bool FramePacer::isGameHalted() const return m_isGameHalted; } -void FramePacer::setLogicTimeScaleFps( Int fps ) -{ - m_logicTimeScaleFPS = fps; -} - Int FramePacer::getLogicTimeScaleFps() const { return m_logicTimeScaleFPS; } -void FramePacer::enableLogicTimeScale( Bool enable ) +void FramePacer::enableLogicTimeScale( Bool enable, Int fps ) { + if (fps >= 0) + { + m_logicTimeScaleFPS = fps; + } m_enableLogicTimeScale = enable; } diff --git a/Core/GameEngine/Source/Common/FrameRateLimit.cpp b/Core/GameEngine/Source/Common/FrameRateLimit.cpp index c625c831ca7..45a19517821 100644 --- a/Core/GameEngine/Source/Common/FrameRateLimit.cpp +++ b/Core/GameEngine/Source/Common/FrameRateLimit.cpp @@ -160,6 +160,7 @@ UnsignedInt LogicTimeScaleFpsPreset::getPrevFpsValue(UnsignedInt value, Unsigned { prevValue = fpsValue; } + break; } } diff --git a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index d9e56b808d2..afb96a72f96 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -228,12 +228,7 @@ bool changeLogicTimeScale(FpsValueChange change) const bool enableTimescale = (logicTimeScaleFps < maxRenderFps); // TheSuperHackers @info Preserve the last real FPS in m_logicTimeScaleFPS so re-enabling timescale resumes from a sane value. - if (enableTimescale) - { - TheFramePacer->setLogicTimeScaleFps(logicTimeScaleFps); - } - - TheFramePacer->enableLogicTimeScale(enableTimescale); + TheFramePacer->enableLogicTimeScale(enableTimescale, enableTimescale ? (Int)logicTimeScaleFps : -1); logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps(); const Real actualLogicTimeScaleRatio = TheFramePacer->getActualLogicTimeScaleRatio(); From 6a51a415ae99ad47d1397782580e5eecb7b20fb1 Mon Sep 17 00:00:00 2001 From: githubawn <115191165+githubawn@users.noreply.github.com> Date: Mon, 3 Aug 2026 00:09:16 +0200 Subject: [PATCH 6/6] move functions to framepacer --- Core/GameEngine/Include/Common/FramePacer.h | 2 + Core/GameEngine/Source/Common/FramePacer.cpp | 37 +++++++++++++++++++ .../GameClient/MessageStream/CommandXlat.cpp | 28 +------------- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/Core/GameEngine/Include/Common/FramePacer.h b/Core/GameEngine/Include/Common/FramePacer.h index 04462bb2232..7f3fa3587cc 100644 --- a/Core/GameEngine/Include/Common/FramePacer.h +++ b/Core/GameEngine/Include/Common/FramePacer.h @@ -42,6 +42,7 @@ class FramePacer void reset(); ///< Move the frame timing anchor to now and predict the next update time from the target frame rate. Call after a long blocking operation so its duration does not leak into the next frame delta. void setFramesPerSecondLimit( Int fps ); ///< Set the update fps limit. + UnsignedInt changeFramesPerSecondLimit( FpsValueChange change ); ///< Step the update fps limit to the next/prev preset, apply it, and return the new value. Int getFramesPerSecondLimit() const; ///< Get the update fps limit. void enableFramesPerSecondLimit( Bool enable ); ///< Enable or disable the update fps limit. Bool isFramesPerSecondLimitEnabled() const; ///< Returns whether the fps limit is enabled here. @@ -58,6 +59,7 @@ class FramePacer Bool isGameHalted() const; Int getLogicTimeScaleFps() const; ///< Get the raw logic time scale fps value. + UnsignedInt changeLogicTimeScaleFps( FpsValueChange change ); ///< Step the logic time scale fps to the next/prev preset, apply and enable/disable it as needed, and return the resulting value. void enableLogicTimeScale( Bool enable, Int fps = -1 ); ///< Enable or disable the logic time scale setup. If fps >= 0, also sets the logic time scale fps before enabling. If disabled, the simulation time scale is bound to the render frame time or network update time. Bool isLogicTimeScaleEnabled() const; ///< Check whether the logic time scale setup is enabled. Int getActualLogicTimeScaleFps(LogicTimeQueryFlags flags = 0) const; ///< Get the real logic time scale fps, depending on the max render fps, network state and enabled state. diff --git a/Core/GameEngine/Source/Common/FramePacer.cpp b/Core/GameEngine/Source/Common/FramePacer.cpp index ffb36179c25..5c7d6432717 100644 --- a/Core/GameEngine/Source/Common/FramePacer.cpp +++ b/Core/GameEngine/Source/Common/FramePacer.cpp @@ -70,6 +70,17 @@ void FramePacer::setFramesPerSecondLimit( Int fps ) m_maxFPS = fps; } +UnsignedInt FramePacer::changeFramesPerSecondLimit( FpsValueChange change ) +{ + UnsignedInt maxRenderFps = getFramesPerSecondLimit(); + maxRenderFps = RenderFpsPreset::changeFpsValue(maxRenderFps, change); + + setFramesPerSecondLimit(maxRenderFps); + TheWritableGlobalData->m_useFpsLimit = (maxRenderFps != RenderFpsPreset::UncappedFpsValue); + + return maxRenderFps; +} + Int FramePacer::getFramesPerSecondLimit() const { return m_maxFPS; @@ -157,6 +168,32 @@ Int FramePacer::getLogicTimeScaleFps() const return m_logicTimeScaleFPS; } +UnsignedInt FramePacer::changeLogicTimeScaleFps( FpsValueChange change ) +{ + const UnsignedInt maxRenderFps = getFramesPerSecondLimit(); + UnsignedInt logicTimeScaleFps = getLogicTimeScaleFps(); + + if (!isLogicTimeScaleEnabled()) + { + logicTimeScaleFps = maxRenderFps; + } + + logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change, maxRenderFps); + + // Ensure logic FPS never exceeds render FPS + if (logicTimeScaleFps > maxRenderFps && logicTimeScaleFps != RenderFpsPreset::UncappedFpsValue) + { + logicTimeScaleFps = maxRenderFps; + } + + const bool enableTimescale = (logicTimeScaleFps < maxRenderFps); + + // TheSuperHackers @info Preserve the last real FPS in m_logicTimeScaleFPS so re-enabling timescale resumes from a sane value. + enableLogicTimeScale(enableTimescale, enableTimescale ? (Int)logicTimeScaleFps : -1); + + return getLogicTimeScaleFps(); +} + void FramePacer::enableLogicTimeScale( Bool enable, Int fps ) { if (fps >= 0) diff --git a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp index afb96a72f96..d9cddc72eb9 100644 --- a/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp +++ b/Core/GameEngine/Source/GameClient/MessageStream/CommandXlat.cpp @@ -182,11 +182,7 @@ Bool hasThingsInProduction(PlayerType playerType) bool changeMaxRenderFps(FpsValueChange change) { - UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit(); - maxRenderFps = RenderFpsPreset::changeFpsValue(maxRenderFps, change); - - TheFramePacer->setFramesPerSecondLimit(maxRenderFps); - TheWritableGlobalData->m_useFpsLimit = (maxRenderFps != RenderFpsPreset::UncappedFpsValue); + const UnsignedInt maxRenderFps = TheFramePacer->changeFramesPerSecondLimit(change); UnicodeString message; @@ -209,27 +205,7 @@ bool changeLogicTimeScale(FpsValueChange change) if (TheNetwork != nullptr) return false; - const UnsignedInt maxRenderFps = TheFramePacer->getFramesPerSecondLimit(); - UnsignedInt logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); - - if (!TheFramePacer->isLogicTimeScaleEnabled()) - { - logicTimeScaleFps = maxRenderFps; - } - - logicTimeScaleFps = LogicTimeScaleFpsPreset::changeFpsValue(logicTimeScaleFps, change, maxRenderFps); - - // Ensure logic FPS never exceeds render FPS - if (logicTimeScaleFps > maxRenderFps && logicTimeScaleFps != RenderFpsPreset::UncappedFpsValue) - { - logicTimeScaleFps = maxRenderFps; - } - - const bool enableTimescale = (logicTimeScaleFps < maxRenderFps); - - // TheSuperHackers @info Preserve the last real FPS in m_logicTimeScaleFPS so re-enabling timescale resumes from a sane value. - TheFramePacer->enableLogicTimeScale(enableTimescale, enableTimescale ? (Int)logicTimeScaleFps : -1); - logicTimeScaleFps = TheFramePacer->getLogicTimeScaleFps(); + const UnsignedInt logicTimeScaleFps = TheFramePacer->changeLogicTimeScaleFps(change); const UnsignedInt actualLogicTimeScaleFps = TheFramePacer->getActualLogicTimeScaleFps(); const Real actualLogicTimeScaleRatio = TheFramePacer->getActualLogicTimeScaleRatio();