From aa5c599c21b7be23e26d25c9e46fdb94ec489196 Mon Sep 17 00:00:00 2001 From: rudkoLA Date: Wed, 17 Jun 2026 16:52:46 +0300 Subject: [PATCH 1/4] add sar_trace_sync --- docs/cvars.md | 1 + src/Features/PlayerTrace.cpp | 39 ++++++++++++++++++++++++++++++------ src/Features/PlayerTrace.hpp | 1 + 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docs/cvars.md b/docs/cvars.md index ba91db5a..4e318157 100644 --- a/docs/cvars.md +++ b/docs/cvars.md @@ -692,6 +692,7 @@ |sar_trace_record|0|Record the trace to a slot. Set to 0 for not recording| |sar_trace_reveal|0|Only draw traces until the specified tick. Set to bbox to draw until the bbox tick.| |sar_trace_show|cmd|sar_trace_show [trace name] - show the trace with the given name| +|sar_trace_sync|cmd|sar_trace_sync - syncs all the hovered traces to the fastest trace.| |sar_trace_teleport_at|cmd|sar_trace_teleport_at \ [player slot] [trace name] - teleports the player at the given trace tick on the given trace ID (defaults to hovered one or the first one ever made) in the given slot (defaults to 0).| |sar_trace_teleport_eye|cmd|sar_trace_teleport_eye \ [player slot] [trace name] - teleports the player to the eye position at the given trace tick on the given trace (defaults to hovered one or the first one ever made) in the given slot (defaults to 0).| |sar_trace_use_shot_eyeoffset|1|Uses eye offset and angles accurate for portal shooting.| diff --git a/src/Features/PlayerTrace.cpp b/src/Features/PlayerTrace.cpp index a1e10821..7f1355f9 100644 --- a/src/Features/PlayerTrace.cpp +++ b/src/Features/PlayerTrace.cpp @@ -16,6 +16,7 @@ #include "Modules/Surface.hpp" #include +#include PlayerTrace *playerTrace; @@ -60,11 +61,11 @@ static int tickInternalToUser(int tick, const Trace &trace) { if (tick == -1) return -1; switch (sar_trace_draw_time.GetInt()) { case 2: - return tick + trace.startSessionTick; + return tick + trace.startSessionTick + trace.tasTickOffset; case 3: - if (trace.startTasTick > 0) return tick + trace.startTasTick; + if (trace.startTasTick > 0) return tick + trace.startTasTick + trace.tasTickOffset; default: // FALLTHROUGH - return tick; + return tick + trace.tasTickOffset; } } @@ -72,11 +73,11 @@ static int tickUserToInternal(int tick, const Trace &trace) { if (tick == -1) return -1; switch (sar_trace_draw_time.GetInt()) { case 2: - return tick - trace.startSessionTick; + return tick - trace.startSessionTick - trace.tasTickOffset; case 3: - if (trace.startTasTick > 0) return tick - trace.startTasTick; + if (trace.startTasTick > 0) return tick - trace.startTasTick - trace.tasTickOffset; default: // FALLTHROUGH - return tick; + return tick - trace.tasTickOffset; } } @@ -1239,6 +1240,32 @@ CON_COMMAND(sar_trace_compare, "sar_trace_compare - compares } } +CON_COMMAND(sar_trace_sync, "sar_trace_sync - syncs all the hovered traces to the fastest trace.\n") { + std::unordered_map trace_ticks; + + int min_tick = INT_MAX; + std::string fastest_trace_name; + + for (auto &h : hovers) { + int tick = h.tick; + + if (tick < min_tick) { + min_tick = tick; + fastest_trace_name = h.trace_name; + } + + trace_ticks[h.trace_name] = tick; + } + + auto fastest_trace = playerTrace->GetTrace(fastest_trace_name); + + for (const auto& [name, tick] : trace_ticks) { + auto trace = playerTrace->GetTrace(name); + + trace->tasTickOffset = min_tick - tick; + } +} + void PlayerTrace::EnterLogScope(const char *name) { if (!playerTrace->ShouldRecord()) return; auto trace = this->GetTrace(sar_trace_record.GetString()); diff --git a/src/Features/PlayerTrace.hpp b/src/Features/PlayerTrace.hpp index 71ab5e35..254cdf70 100644 --- a/src/Features/PlayerTrace.hpp +++ b/src/Features/PlayerTrace.hpp @@ -48,6 +48,7 @@ struct PortalLocations { struct Trace { int startSessionTick; int startTasTick; + int tasTickOffset = 0; std::vector positions[2]; std::vector eyepos[2]; std::vector angles[2]; From 8d6a362b8fb0cb92fe1e671b4a17357d57339efd Mon Sep 17 00:00:00 2001 From: rudkoLA Date: Wed, 17 Jun 2026 21:30:18 +0300 Subject: [PATCH 2/4] clean up code --- src/Features/PlayerTrace.cpp | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/Features/PlayerTrace.cpp b/src/Features/PlayerTrace.cpp index 7f1355f9..fbd984d3 100644 --- a/src/Features/PlayerTrace.cpp +++ b/src/Features/PlayerTrace.cpp @@ -1241,28 +1241,16 @@ CON_COMMAND(sar_trace_compare, "sar_trace_compare - compares } CON_COMMAND(sar_trace_sync, "sar_trace_sync - syncs all the hovered traces to the fastest trace.\n") { - std::unordered_map trace_ticks; - - int min_tick = INT_MAX; - std::string fastest_trace_name; + size_t min_tick = SIZE_MAX; for (auto &h : hovers) { - int tick = h.tick; - - if (tick < min_tick) { - min_tick = tick; - fastest_trace_name = h.trace_name; - } - - trace_ticks[h.trace_name] = tick; + min_tick = std::min(min_tick, h.tick); } - auto fastest_trace = playerTrace->GetTrace(fastest_trace_name); - - for (const auto& [name, tick] : trace_ticks) { - auto trace = playerTrace->GetTrace(name); + for (auto &h : hovers) { + auto trace = playerTrace->GetTrace(h.trace_name); - trace->tasTickOffset = min_tick - tick; + trace->tasTickOffset = min_tick - h.tick; } } From 299a7f2857e7133272235d7e7ef0e64c470850da Mon Sep 17 00:00:00 2001 From: rudkoLA Date: Thu, 18 Jun 2026 00:11:03 +0300 Subject: [PATCH 3/4] make offset preserve through trace recording via trace name --- src/Features/PlayerTrace.cpp | 16 +++++++++++++++- src/Features/PlayerTrace.hpp | 4 ++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/Features/PlayerTrace.cpp b/src/Features/PlayerTrace.cpp index fbd984d3..010c8039 100644 --- a/src/Features/PlayerTrace.cpp +++ b/src/Features/PlayerTrace.cpp @@ -137,6 +137,12 @@ void PlayerTrace::AddPoint(std::string trace_name, void *player, int slot, bool if (traces.count(trace_name) == 0) { traces[trace_name] = Trace(); traces[trace_name].startSessionTick = session->GetTick(); + + auto it = playerTrace->tickOffsets.find(trace_name); + + if (it != playerTrace->tickOffsets.end()) { + traces[trace_name].tasTickOffset = it->second; + } } Trace &trace = traces[trace_name]; @@ -890,6 +896,10 @@ int PlayerTrace::GetTasTraceTick() { return max_tas_tick; } +void PlayerTrace::SetTickOffset(std::string &trace_name, int offset) { + playerTrace->tickOffsets[trace_name] = offset; +} + HUD_ELEMENT2(trace, "0", "Draws info about current trace bbox tick.\n", HudType_InGame | HudType_Paused) { if (!sv_cheats.GetBool()) return; playerTrace->DrawTraceHud(ctx); @@ -1250,7 +1260,11 @@ CON_COMMAND(sar_trace_sync, "sar_trace_sync - syncs all the hovered traces to th for (auto &h : hovers) { auto trace = playerTrace->GetTrace(h.trace_name); - trace->tasTickOffset = min_tick - h.tick; + auto offset = min_tick - h.tick; + + trace->tasTickOffset = offset; + + playerTrace->SetTickOffset(h.trace_name, offset); } } diff --git a/src/Features/PlayerTrace.hpp b/src/Features/PlayerTrace.hpp index 254cdf70..4dc9b32f 100644 --- a/src/Features/PlayerTrace.hpp +++ b/src/Features/PlayerTrace.hpp @@ -70,6 +70,8 @@ class PlayerTrace : public Feature { private: // In order to arbitrarily number traces std::map traces; + // Universal map for trace tick offsets to be able to preserve them between trace recordings + std::unordered_map tickOffsets; std::string lastRecordedTrace; public: PlayerTrace(); @@ -115,6 +117,8 @@ class PlayerTrace : public Feature { void CheckTraceChanged(); // Get the current trace bbox tick for TAS stuff, or -1 if there isn't one int GetTasTraceTick(); + // Set tick offset globally + void SetTickOffset(std::string &trace_name, int offset); // Returns an identifier for the scope which should be passed to ExitLogScope void EnterLogScope(const char *name); From 50983b7b7abcec6d7ad88cc539aac0afbfadfa6d Mon Sep 17 00:00:00 2001 From: rudkoLA Date: Thu, 18 Jun 2026 00:46:46 +0300 Subject: [PATCH 4/4] add trace sync reset commands --- docs/cvars.md | 2 ++ src/Features/PlayerTrace.cpp | 26 ++++++++++++++++++++++++++ src/Features/PlayerTrace.hpp | 2 ++ 3 files changed, 30 insertions(+) diff --git a/docs/cvars.md b/docs/cvars.md index 4e318157..ddb8bce6 100644 --- a/docs/cvars.md +++ b/docs/cvars.md @@ -693,6 +693,8 @@ |sar_trace_reveal|0|Only draw traces until the specified tick. Set to bbox to draw until the bbox tick.| |sar_trace_show|cmd|sar_trace_show [trace name] - show the trace with the given name| |sar_trace_sync|cmd|sar_trace_sync - syncs all the hovered traces to the fastest trace.| +|sar_trace_sync_reset|cmd|sar_trace_sync_reset \ - resets the sync of the player trace by the given name.| +|sar_trace_sync_reset_all|cmd|sar_trace_sync_reset_all - resets the sync of all the player traces.| |sar_trace_teleport_at|cmd|sar_trace_teleport_at \ [player slot] [trace name] - teleports the player at the given trace tick on the given trace ID (defaults to hovered one or the first one ever made) in the given slot (defaults to 0).| |sar_trace_teleport_eye|cmd|sar_trace_teleport_eye \ [player slot] [trace name] - teleports the player to the eye position at the given trace tick on the given trace (defaults to hovered one or the first one ever made) in the given slot (defaults to 0).| |sar_trace_use_shot_eyeoffset|1|Uses eye offset and angles accurate for portal shooting.| diff --git a/src/Features/PlayerTrace.cpp b/src/Features/PlayerTrace.cpp index 010c8039..7deb1aa6 100644 --- a/src/Features/PlayerTrace.cpp +++ b/src/Features/PlayerTrace.cpp @@ -900,6 +900,16 @@ void PlayerTrace::SetTickOffset(std::string &trace_name, int offset) { playerTrace->tickOffsets[trace_name] = offset; } +void PlayerTrace::ResetAllTraceOffsets() { + for (auto it = playerTrace->traces.begin(); it != playerTrace->traces.end(); ++it) { + Trace &trace = it->second; + + trace.tasTickOffset = 0; + } + + playerTrace->tickOffsets.clear(); +} + HUD_ELEMENT2(trace, "0", "Draws info about current trace bbox tick.\n", HudType_InGame | HudType_Paused) { if (!sv_cheats.GetBool()) return; playerTrace->DrawTraceHud(ctx); @@ -1268,6 +1278,22 @@ CON_COMMAND(sar_trace_sync, "sar_trace_sync - syncs all the hovered traces to th } } +CON_COMMAND(sar_trace_sync_reset_all, "sar_trace_sync_reset_all - resets the sync of all the player traces.\n") { + playerTrace->ResetAllTraceOffsets(); +} + +CON_COMMAND(sar_trace_sync_reset, "sar_trace_sync_reset - resets the sync of the player trace by the given name.\n") { + if (args.ArgC() < 2) { + return console->Print(sar_trace_sync_reset.ThisPtr()->m_pszHelpString); + } + + std::string trace_name = args[1]; + + playerTrace->GetTrace(trace_name)->tasTickOffset = 0; + + playerTrace->SetTickOffset(trace_name, 0); +} + void PlayerTrace::EnterLogScope(const char *name) { if (!playerTrace->ShouldRecord()) return; auto trace = this->GetTrace(sar_trace_record.GetString()); diff --git a/src/Features/PlayerTrace.hpp b/src/Features/PlayerTrace.hpp index 4dc9b32f..67cf21bc 100644 --- a/src/Features/PlayerTrace.hpp +++ b/src/Features/PlayerTrace.hpp @@ -119,6 +119,8 @@ class PlayerTrace : public Feature { int GetTasTraceTick(); // Set tick offset globally void SetTickOffset(std::string &trace_name, int offset); + // Reset all offsets + void ResetAllTraceOffsets(); // Returns an identifier for the scope which should be passed to ExitLogScope void EnterLogScope(const char *name);