From 38e9c41ebb68b4b047d32750b169668c0b336917 Mon Sep 17 00:00:00 2001 From: PortalRex <35444194+PortalRex@users.noreply.github.com> Date: Sun, 2 Aug 2026 11:25:23 -0500 Subject: [PATCH 1/2] feat: timeline pb only ``sar_timeline_pb_only`` Only add pbs to the timeline. --- docs/cvars.md | 1 + src/Features/SteamTimeline.cpp | 52 ++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/docs/cvars.md b/docs/cvars.md index 29afa209e..2de40470a 100644 --- a/docs/cvars.md +++ b/docs/cvars.md @@ -640,6 +640,7 @@ |sar_time_demo|cmd|sar_time_demo \ - parses a demo and prints some information about it| |sar_time_demo_dev|0|Printing mode when using sar_time_demo.
0 = Default,
1 = Console commands,
2 = Console commands & packets.| |sar_time_demos|cmd|sar_time_demos \ [demo_name2]... - parses multiple demos and prints the total sum of them| +|sar_timeline_pb_only|0|Only add speedruns to the Steam Timeline when they are a personal best.| |sar_timeline_show_completed|0|Only show speedrun starts and splits with matching finishes.| |sar_timeline_splits|1|Add split markers to the Steam Timeline.| |sar_timer_always_running|1|Timer will save current value when disconnecting.| diff --git a/src/Features/SteamTimeline.cpp b/src/Features/SteamTimeline.cpp index f5356f849..4f48ae70f 100644 --- a/src/Features/SteamTimeline.cpp +++ b/src/Features/SteamTimeline.cpp @@ -8,6 +8,7 @@ /* Timeline can get cluttered up (especially at long recording lengths), nice to have an option to disable adding splits. */ Variable sar_timeline_splits("sar_timeline_splits", "1", "Add split markers to the Steam Timeline.\n"); Variable sar_timeline_show_completed("sar_timeline_show_completed", "0", "Only show speedrun starts and splits with matching finishes.\n"); +Variable sar_timeline_pb_only("sar_timeline_pb_only", "0", "Only add speedruns to the Steam Timeline when they are a personal best.\n"); Timeline *timeline; @@ -17,13 +18,18 @@ Timeline::Timeline() { static std::chrono::time_point g_speedrunStart; static std::vector> g_pendingSplits; +static float g_pendingPbFinishOffset; +static std::string g_pendingPbFinishTime; +static std::chrono::time_point g_pendingPbSpeedrunStart; +static std::vector> g_pendingPbSplits; +static bool g_hasPendingPbRun = false; void Timeline::StartSpeedrun() { if (!steam->hasLoaded) return; g_speedrunStart = std::chrono::system_clock::now(); g_pendingSplits.clear(); - if (sar_timeline_show_completed.GetBool()) return; + if (sar_timeline_show_completed.GetBool() || sar_timeline_pb_only.GetBool()) return; steam->g_timeline->AddTimelineEvent("steam_timer", "Speedrun Start", "", 1, 0.0f, 0.0f, k_ETimelineEventClipPriority_Standard); } @@ -31,7 +37,7 @@ void Timeline::Split(std::string name, std::string time) { if (!steam->hasLoaded) return; std::chrono::duration currentOffset = std::chrono::system_clock::now() - g_speedrunStart; if (sar_timeline_splits.GetBool()) { - if (sar_timeline_show_completed.GetBool()) { + if (sar_timeline_show_completed.GetBool() || sar_timeline_pb_only.GetBool()) { g_pendingSplits.push_back({name, time, currentOffset.count()}); } else { steam->g_timeline->AddTimelineEvent("steam_bolt", name.c_str(), time.c_str(), 0, 0.0f, 0.0f, k_ETimelineEventClipPriority_None); @@ -57,6 +63,16 @@ ON_EVENT(SPEEDRUN_FINISH) { auto fl_time = SpeedrunTimer::GetTotalTicks() * engine->GetIPT(); auto time = SpeedrunTimer::Format(fl_time); + if (sar_timeline_pb_only.GetBool()) { + g_pendingPbFinishOffset = offset.count(); + g_pendingPbFinishTime = time; + g_pendingPbSpeedrunStart = g_speedrunStart; + g_pendingPbSplits = g_pendingSplits; + g_hasPendingPbRun = true; + g_pendingSplits.clear(); + return; + } + if (sar_timeline_show_completed.GetBool()) { steam->g_timeline->AddTimelineEvent("steam_timer", "Speedrun Start", "", 1, -offset.count(), 0.0f, k_ETimelineEventClipPriority_Standard); @@ -80,8 +96,38 @@ ON_EVENT(SPEEDRUN_FINISH) { ON_EVENT(MAYBE_AUTOSUBMIT) { if (!steam->hasLoaded) return; - if (!event.pb) + if (!event.pb) { + g_pendingPbSplits.clear(); + g_hasPendingPbRun = false; return; + } + + if (sar_timeline_pb_only.GetBool() && g_hasPendingPbRun) { + std::chrono::duration offset = std::chrono::system_clock::now() - g_pendingPbSpeedrunStart; + float finishOffset = g_pendingPbFinishOffset - offset.count(); + + steam->g_timeline->AddTimelineEvent("steam_timer", "Speedrun Start", "", 1, -offset.count(), 0.0f, k_ETimelineEventClipPriority_Standard); + + for (const auto &[splitName, splitTime, splitOffset] : g_pendingPbSplits) { + steam->g_timeline->AddTimelineEvent( + "steam_bolt", + splitName.c_str(), + splitTime.c_str(), + 0, + splitOffset - offset.count(), + 0.0f, + k_ETimelineEventClipPriority_None); + } + g_pendingPbSplits.clear(); + + steam->g_timeline->SetTimelineStateDescription(("Speedrun " + g_pendingPbFinishTime).c_str(), -offset.count()); + steam->g_timeline->ClearTimelineStateDescription(finishOffset); + steam->g_timeline->AddTimelineEvent("steam_crown", "Personal Best", SpeedrunTimer::Format(event.score / 100.0f).c_str(), 2, finishOffset, 0.0f, k_ETimelineEventClipPriority_Featured); + g_hasPendingPbRun = false; + return; + } + g_pendingPbSplits.clear(); + g_hasPendingPbRun = false; steam->g_timeline->AddTimelineEvent("steam_crown", "Personal Best", SpeedrunTimer::Format(event.score / 100.0f).c_str(), 2, 0.0f, 0.0f, k_ETimelineEventClipPriority_Featured); } From 050fdd3be531bf6bb7c6019c646166c3ea010b16 Mon Sep 17 00:00:00 2001 From: AMJ <69196954+ThisAMJ@users.noreply.github.com> Date: Tue, 4 Aug 2026 11:32:48 +1000 Subject: [PATCH 2/2] fix: `sar_timeline_show_completed 2` --- docs/cvars.md | 3 +-- src/Features/SteamTimeline.cpp | 11 +++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/cvars.md b/docs/cvars.md index 2de40470a..ec4559557 100644 --- a/docs/cvars.md +++ b/docs/cvars.md @@ -640,8 +640,7 @@ |sar_time_demo|cmd|sar_time_demo \ - parses a demo and prints some information about it| |sar_time_demo_dev|0|Printing mode when using sar_time_demo.
0 = Default,
1 = Console commands,
2 = Console commands & packets.| |sar_time_demos|cmd|sar_time_demos \ [demo_name2]... - parses multiple demos and prints the total sum of them| -|sar_timeline_pb_only|0|Only add speedruns to the Steam Timeline when they are a personal best.| -|sar_timeline_show_completed|0|Only show speedrun starts and splits with matching finishes.| +|sar_timeline_show_completed|0|Only show speedrun starts and splits with matching finishes. Set to 2 to only show Personal Bests.| |sar_timeline_splits|1|Add split markers to the Steam Timeline.| |sar_timer_always_running|1|Timer will save current value when disconnecting.| |sar_timer_result|cmd|sar_timer_result - prints result of timer| diff --git a/src/Features/SteamTimeline.cpp b/src/Features/SteamTimeline.cpp index 4f48ae70f..8be7fbcb0 100644 --- a/src/Features/SteamTimeline.cpp +++ b/src/Features/SteamTimeline.cpp @@ -7,8 +7,7 @@ /* Timeline can get cluttered up (especially at long recording lengths), nice to have an option to disable adding splits. */ Variable sar_timeline_splits("sar_timeline_splits", "1", "Add split markers to the Steam Timeline.\n"); -Variable sar_timeline_show_completed("sar_timeline_show_completed", "0", "Only show speedrun starts and splits with matching finishes.\n"); -Variable sar_timeline_pb_only("sar_timeline_pb_only", "0", "Only add speedruns to the Steam Timeline when they are a personal best.\n"); +Variable sar_timeline_show_completed("sar_timeline_show_completed", "0", "Only show speedrun starts and splits with matching finishes. Set to 2 to only show Personal Bests.\n"); Timeline *timeline; @@ -29,7 +28,7 @@ void Timeline::StartSpeedrun() { g_speedrunStart = std::chrono::system_clock::now(); g_pendingSplits.clear(); - if (sar_timeline_show_completed.GetBool() || sar_timeline_pb_only.GetBool()) return; + if (sar_timeline_show_completed.GetBool()) return; steam->g_timeline->AddTimelineEvent("steam_timer", "Speedrun Start", "", 1, 0.0f, 0.0f, k_ETimelineEventClipPriority_Standard); } @@ -37,7 +36,7 @@ void Timeline::Split(std::string name, std::string time) { if (!steam->hasLoaded) return; std::chrono::duration currentOffset = std::chrono::system_clock::now() - g_speedrunStart; if (sar_timeline_splits.GetBool()) { - if (sar_timeline_show_completed.GetBool() || sar_timeline_pb_only.GetBool()) { + if (sar_timeline_show_completed.GetBool()) { g_pendingSplits.push_back({name, time, currentOffset.count()}); } else { steam->g_timeline->AddTimelineEvent("steam_bolt", name.c_str(), time.c_str(), 0, 0.0f, 0.0f, k_ETimelineEventClipPriority_None); @@ -63,7 +62,7 @@ ON_EVENT(SPEEDRUN_FINISH) { auto fl_time = SpeedrunTimer::GetTotalTicks() * engine->GetIPT(); auto time = SpeedrunTimer::Format(fl_time); - if (sar_timeline_pb_only.GetBool()) { + if (sar_timeline_show_completed.GetInt() >= 2) { g_pendingPbFinishOffset = offset.count(); g_pendingPbFinishTime = time; g_pendingPbSpeedrunStart = g_speedrunStart; @@ -102,7 +101,7 @@ ON_EVENT(MAYBE_AUTOSUBMIT) { return; } - if (sar_timeline_pb_only.GetBool() && g_hasPendingPbRun) { + if (sar_timeline_show_completed.GetInt() >= 2 && g_hasPendingPbRun) { std::chrono::duration offset = std::chrono::system_clock::now() - g_pendingPbSpeedrunStart; float finishOffset = g_pendingPbFinishOffset - offset.count();