Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play - #374

Merged
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio
Aug 8, 2026
Merged

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play#374
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio

Conversation

@betsruner

Copy link
Copy Markdown
Member

Fixes#19

When sar_disable_challenge_stats_hud was set to -1 sometimes (not consistently) when the player got a Personal Best it would use the incorrect line of dialogue "Maybe I was wrong about you".

This happened because SAR detours CHud::GetName to return an empty string. Due to that when the game searched for the challenge stats hud panel it was unable to find it, which of course means that it couldn't set its open leaderboard flag. The leaderboards OnThink function would normally then see that flag and open the leaderboard which would eventually asynchronously preload the leaderboard data. Since the data is never preloaded, when the level is completed the end screen tries to create the leaderboard and query for the scores. That causes a race, if the time is updated first then the query sees the new score as equal to the current score instead of an improvement. If the query finishes first then the game has the correct data so it will play the appropriate dialogue.

The fix is whenever the original +leaderboard 4 callback is run SAR then directly calls CPortalLeaderboardManager::FindOrCreateLeaderboard for the current map. This ends up preloading the data preventing the race and causing the correct dialogue to play 100% of the time. This is the same async preload the leaderboard window would have done anyway so there shouldn't be any particular issues with resolving it like this.

I tested the change pretty thoroughly. When initially figuring out what was causing the incorrect dialogue to play I learned you could almost always get bad dialogue to play if you completed a run that didn't PB prior to PBing. So I tested it doing that + some runs when I just got a PB right away just in case. Did that on both Windows and Linux, Cooperative and Singleplayer. With the fix I've never had the incorrect dialogue play.

Marginally interesting, the original fix of just patching the dialogue after the fact (not the current implementation) actually worked on Windows (both Coop and SP), but it didn't work on Linux. The clankers and I couldn't find any platform specific reason why that was the case. That's why I decided to switch to doing it the more correct way (actually preloading the data the same way the game would).

@ThisAMJ
ThisAMJ merged commit 55f3dab into p2sr:masterAug 8, 2026
2 checks passed
ThisAMJ pushed a commit that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using sar_disable_challenge_stats_hud 1 results in same voice line

2 participants

@betsruner@ThisAMJ
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play - #374

Merged
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio
Aug 8, 2026
Merged

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play#374
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio

Conversation

@betsruner

Copy link
Copy Markdown
Member

Fixes#19

When sar_disable_challenge_stats_hud was set to -1 sometimes (not consistently) when the player got a Personal Best it would use the incorrect line of dialogue "Maybe I was wrong about you".

This happened because SAR detours CHud::GetName to return an empty string. Due to that when the game searched for the challenge stats hud panel it was unable to find it, which of course means that it couldn't set its open leaderboard flag. The leaderboards OnThink function would normally then see that flag and open the leaderboard which would eventually asynchronously preload the leaderboard data. Since the data is never preloaded, when the level is completed the end screen tries to create the leaderboard and query for the scores. That causes a race, if the time is updated first then the query sees the new score as equal to the current score instead of an improvement. If the query finishes first then the game has the correct data so it will play the appropriate dialogue.

The fix is whenever the original +leaderboard 4 callback is run SAR then directly calls CPortalLeaderboardManager::FindOrCreateLeaderboard for the current map. This ends up preloading the data preventing the race and causing the correct dialogue to play 100% of the time. This is the same async preload the leaderboard window would have done anyway so there shouldn't be any particular issues with resolving it like this.

I tested the change pretty thoroughly. When initially figuring out what was causing the incorrect dialogue to play I learned you could almost always get bad dialogue to play if you completed a run that didn't PB prior to PBing. So I tested it doing that + some runs when I just got a PB right away just in case. Did that on both Windows and Linux, Cooperative and Singleplayer. With the fix I've never had the incorrect dialogue play.

Marginally interesting, the original fix of just patching the dialogue after the fact (not the current implementation) actually worked on Windows (both Coop and SP), but it didn't work on Linux. The clankers and I couldn't find any platform specific reason why that was the case. That's why I decided to switch to doing it the more correct way (actually preloading the data the same way the game would).

@ThisAMJ
ThisAMJ merged commit 55f3dab into p2sr:masterAug 8, 2026
2 checks passed
ThisAMJ pushed a commit that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using sar_disable_challenge_stats_hud 1 results in same voice line

2 participants

@betsruner@ThisAMJ
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play - #374

Merged
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio
Aug 8, 2026
Merged

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play#374
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio

Conversation

@betsruner

Copy link
Copy Markdown
Member

Fixes#19

When sar_disable_challenge_stats_hud was set to -1 sometimes (not consistently) when the player got a Personal Best it would use the incorrect line of dialogue "Maybe I was wrong about you".

This happened because SAR detours CHud::GetName to return an empty string. Due to that when the game searched for the challenge stats hud panel it was unable to find it, which of course means that it couldn't set its open leaderboard flag. The leaderboards OnThink function would normally then see that flag and open the leaderboard which would eventually asynchronously preload the leaderboard data. Since the data is never preloaded, when the level is completed the end screen tries to create the leaderboard and query for the scores. That causes a race, if the time is updated first then the query sees the new score as equal to the current score instead of an improvement. If the query finishes first then the game has the correct data so it will play the appropriate dialogue.

The fix is whenever the original +leaderboard 4 callback is run SAR then directly calls CPortalLeaderboardManager::FindOrCreateLeaderboard for the current map. This ends up preloading the data preventing the race and causing the correct dialogue to play 100% of the time. This is the same async preload the leaderboard window would have done anyway so there shouldn't be any particular issues with resolving it like this.

I tested the change pretty thoroughly. When initially figuring out what was causing the incorrect dialogue to play I learned you could almost always get bad dialogue to play if you completed a run that didn't PB prior to PBing. So I tested it doing that + some runs when I just got a PB right away just in case. Did that on both Windows and Linux, Cooperative and Singleplayer. With the fix I've never had the incorrect dialogue play.

Marginally interesting, the original fix of just patching the dialogue after the fact (not the current implementation) actually worked on Windows (both Coop and SP), but it didn't work on Linux. The clankers and I couldn't find any platform specific reason why that was the case. That's why I decided to switch to doing it the more correct way (actually preloading the data the same way the game would).

@ThisAMJ
ThisAMJ merged commit 55f3dab into p2sr:masterAug 8, 2026
2 checks passed
ThisAMJ pushed a commit that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using sar_disable_challenge_stats_hud 1 results in same voice line

2 participants

@betsruner@ThisAMJ
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play - #374

Merged
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio
Aug 8, 2026
Merged

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play#374
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio

Conversation

@betsruner

Copy link
Copy Markdown
Member

Fixes#19

When sar_disable_challenge_stats_hud was set to -1 sometimes (not consistently) when the player got a Personal Best it would use the incorrect line of dialogue "Maybe I was wrong about you".

This happened because SAR detours CHud::GetName to return an empty string. Due to that when the game searched for the challenge stats hud panel it was unable to find it, which of course means that it couldn't set its open leaderboard flag. The leaderboards OnThink function would normally then see that flag and open the leaderboard which would eventually asynchronously preload the leaderboard data. Since the data is never preloaded, when the level is completed the end screen tries to create the leaderboard and query for the scores. That causes a race, if the time is updated first then the query sees the new score as equal to the current score instead of an improvement. If the query finishes first then the game has the correct data so it will play the appropriate dialogue.

The fix is whenever the original +leaderboard 4 callback is run SAR then directly calls CPortalLeaderboardManager::FindOrCreateLeaderboard for the current map. This ends up preloading the data preventing the race and causing the correct dialogue to play 100% of the time. This is the same async preload the leaderboard window would have done anyway so there shouldn't be any particular issues with resolving it like this.

I tested the change pretty thoroughly. When initially figuring out what was causing the incorrect dialogue to play I learned you could almost always get bad dialogue to play if you completed a run that didn't PB prior to PBing. So I tested it doing that + some runs when I just got a PB right away just in case. Did that on both Windows and Linux, Cooperative and Singleplayer. With the fix I've never had the incorrect dialogue play.

Marginally interesting, the original fix of just patching the dialogue after the fact (not the current implementation) actually worked on Windows (both Coop and SP), but it didn't work on Linux. The clankers and I couldn't find any platform specific reason why that was the case. That's why I decided to switch to doing it the more correct way (actually preloading the data the same way the game would).

@ThisAMJ
ThisAMJ merged commit 55f3dab into p2sr:masterAug 8, 2026
2 checks passed
ThisAMJ pushed a commit that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using sar_disable_challenge_stats_hud 1 results in same voice line

2 participants

@betsruner@ThisAMJ
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play - #374

Merged
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio
Aug 8, 2026
Merged

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play#374
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio

Conversation

@betsruner

Copy link
Copy Markdown
Member

Fixes#19

When sar_disable_challenge_stats_hud was set to -1 sometimes (not consistently) when the player got a Personal Best it would use the incorrect line of dialogue "Maybe I was wrong about you".

This happened because SAR detours CHud::GetName to return an empty string. Due to that when the game searched for the challenge stats hud panel it was unable to find it, which of course means that it couldn't set its open leaderboard flag. The leaderboards OnThink function would normally then see that flag and open the leaderboard which would eventually asynchronously preload the leaderboard data. Since the data is never preloaded, when the level is completed the end screen tries to create the leaderboard and query for the scores. That causes a race, if the time is updated first then the query sees the new score as equal to the current score instead of an improvement. If the query finishes first then the game has the correct data so it will play the appropriate dialogue.

The fix is whenever the original +leaderboard 4 callback is run SAR then directly calls CPortalLeaderboardManager::FindOrCreateLeaderboard for the current map. This ends up preloading the data preventing the race and causing the correct dialogue to play 100% of the time. This is the same async preload the leaderboard window would have done anyway so there shouldn't be any particular issues with resolving it like this.

I tested the change pretty thoroughly. When initially figuring out what was causing the incorrect dialogue to play I learned you could almost always get bad dialogue to play if you completed a run that didn't PB prior to PBing. So I tested it doing that + some runs when I just got a PB right away just in case. Did that on both Windows and Linux, Cooperative and Singleplayer. With the fix I've never had the incorrect dialogue play.

Marginally interesting, the original fix of just patching the dialogue after the fact (not the current implementation) actually worked on Windows (both Coop and SP), but it didn't work on Linux. The clankers and I couldn't find any platform specific reason why that was the case. That's why I decided to switch to doing it the more correct way (actually preloading the data the same way the game would).

@ThisAMJ
ThisAMJ merged commit 55f3dab into p2sr:masterAug 8, 2026
2 checks passed
ThisAMJ pushed a commit that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using sar_disable_challenge_stats_hud 1 results in same voice line

2 participants

@betsruner@ThisAMJ
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play - #374

Merged
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio
Aug 8, 2026
Merged

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play#374
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio

Conversation

@betsruner

Copy link
Copy Markdown
Member

Fixes#19

When sar_disable_challenge_stats_hud was set to -1 sometimes (not consistently) when the player got a Personal Best it would use the incorrect line of dialogue "Maybe I was wrong about you".

This happened because SAR detours CHud::GetName to return an empty string. Due to that when the game searched for the challenge stats hud panel it was unable to find it, which of course means that it couldn't set its open leaderboard flag. The leaderboards OnThink function would normally then see that flag and open the leaderboard which would eventually asynchronously preload the leaderboard data. Since the data is never preloaded, when the level is completed the end screen tries to create the leaderboard and query for the scores. That causes a race, if the time is updated first then the query sees the new score as equal to the current score instead of an improvement. If the query finishes first then the game has the correct data so it will play the appropriate dialogue.

The fix is whenever the original +leaderboard 4 callback is run SAR then directly calls CPortalLeaderboardManager::FindOrCreateLeaderboard for the current map. This ends up preloading the data preventing the race and causing the correct dialogue to play 100% of the time. This is the same async preload the leaderboard window would have done anyway so there shouldn't be any particular issues with resolving it like this.

I tested the change pretty thoroughly. When initially figuring out what was causing the incorrect dialogue to play I learned you could almost always get bad dialogue to play if you completed a run that didn't PB prior to PBing. So I tested it doing that + some runs when I just got a PB right away just in case. Did that on both Windows and Linux, Cooperative and Singleplayer. With the fix I've never had the incorrect dialogue play.

Marginally interesting, the original fix of just patching the dialogue after the fact (not the current implementation) actually worked on Windows (both Coop and SP), but it didn't work on Linux. The clankers and I couldn't find any platform specific reason why that was the case. That's why I decided to switch to doing it the more correct way (actually preloading the data the same way the game would).

@ThisAMJ
ThisAMJ merged commit 55f3dab into p2sr:masterAug 8, 2026
2 checks passed
ThisAMJ pushed a commit that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using sar_disable_challenge_stats_hud 1 results in same voice line

2 participants

@betsruner@ThisAMJ
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play - #374

Merged
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio
Aug 8, 2026
Merged

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play#374
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio

Conversation

@betsruner

Copy link
Copy Markdown
Member

Fixes#19

When sar_disable_challenge_stats_hud was set to -1 sometimes (not consistently) when the player got a Personal Best it would use the incorrect line of dialogue "Maybe I was wrong about you".

This happened because SAR detours CHud::GetName to return an empty string. Due to that when the game searched for the challenge stats hud panel it was unable to find it, which of course means that it couldn't set its open leaderboard flag. The leaderboards OnThink function would normally then see that flag and open the leaderboard which would eventually asynchronously preload the leaderboard data. Since the data is never preloaded, when the level is completed the end screen tries to create the leaderboard and query for the scores. That causes a race, if the time is updated first then the query sees the new score as equal to the current score instead of an improvement. If the query finishes first then the game has the correct data so it will play the appropriate dialogue.

The fix is whenever the original +leaderboard 4 callback is run SAR then directly calls CPortalLeaderboardManager::FindOrCreateLeaderboard for the current map. This ends up preloading the data preventing the race and causing the correct dialogue to play 100% of the time. This is the same async preload the leaderboard window would have done anyway so there shouldn't be any particular issues with resolving it like this.

I tested the change pretty thoroughly. When initially figuring out what was causing the incorrect dialogue to play I learned you could almost always get bad dialogue to play if you completed a run that didn't PB prior to PBing. So I tested it doing that + some runs when I just got a PB right away just in case. Did that on both Windows and Linux, Cooperative and Singleplayer. With the fix I've never had the incorrect dialogue play.

Marginally interesting, the original fix of just patching the dialogue after the fact (not the current implementation) actually worked on Windows (both Coop and SP), but it didn't work on Linux. The clankers and I couldn't find any platform specific reason why that was the case. That's why I decided to switch to doing it the more correct way (actually preloading the data the same way the game would).

@ThisAMJ
ThisAMJ merged commit 55f3dab into p2sr:masterAug 8, 2026
2 checks passed
ThisAMJ pushed a commit that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using sar_disable_challenge_stats_hud 1 results in same voice line

2 participants

@betsruner@ThisAMJ
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play - #374

Merged
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio
Aug 8, 2026
Merged

Fix: sar_disable_challenge_stats_hud -1 causes incorrect ending dialogue to play#374
ThisAMJ merged 2 commits into
p2sr:masterfrom
betsruner:stats_hud_audio

Conversation

@betsruner

Copy link
Copy Markdown
Member

Fixes#19

When sar_disable_challenge_stats_hud was set to -1 sometimes (not consistently) when the player got a Personal Best it would use the incorrect line of dialogue "Maybe I was wrong about you".

This happened because SAR detours CHud::GetName to return an empty string. Due to that when the game searched for the challenge stats hud panel it was unable to find it, which of course means that it couldn't set its open leaderboard flag. The leaderboards OnThink function would normally then see that flag and open the leaderboard which would eventually asynchronously preload the leaderboard data. Since the data is never preloaded, when the level is completed the end screen tries to create the leaderboard and query for the scores. That causes a race, if the time is updated first then the query sees the new score as equal to the current score instead of an improvement. If the query finishes first then the game has the correct data so it will play the appropriate dialogue.

The fix is whenever the original +leaderboard 4 callback is run SAR then directly calls CPortalLeaderboardManager::FindOrCreateLeaderboard for the current map. This ends up preloading the data preventing the race and causing the correct dialogue to play 100% of the time. This is the same async preload the leaderboard window would have done anyway so there shouldn't be any particular issues with resolving it like this.

I tested the change pretty thoroughly. When initially figuring out what was causing the incorrect dialogue to play I learned you could almost always get bad dialogue to play if you completed a run that didn't PB prior to PBing. So I tested it doing that + some runs when I just got a PB right away just in case. Did that on both Windows and Linux, Cooperative and Singleplayer. With the fix I've never had the incorrect dialogue play.

Marginally interesting, the original fix of just patching the dialogue after the fact (not the current implementation) actually worked on Windows (both Coop and SP), but it didn't work on Linux. The clankers and I couldn't find any platform specific reason why that was the case. That's why I decided to switch to doing it the more correct way (actually preloading the data the same way the game would).

@ThisAMJ
ThisAMJ merged commit 55f3dab into p2sr:masterAug 8, 2026
2 checks passed
ThisAMJ pushed a commit that referenced this pull request Aug 8, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using sar_disable_challenge_stats_hud 1 results in same voice line

2 participants

@betsruner@ThisAMJ