Skip to content

Conversation

@Caball009
Copy link

@Caball009 Caball009 commented Jan 7, 2026

Scripted audio events are synchronized across clients. That means that if the script execution fails for some reason (e.g. caused by another bug), there's a CRC mismatch. This PR changes that, so that scripted audio events are still likely to be the same across clients but script execution failure does not result in a mismatch.

TODO:

@Caball009 Caball009 added Audio Is audio related Major Severity: Minor < Major < Critical < Blocker NoRetail This fix or change is not applicable with Retail game compatibility Script Is related to Script Engine, SCB labels Jan 7, 2026
@Caball009 Caball009 changed the title feat(logic): Decouple scripted audio events from CRC calculation feat(logic): Decouple scripted audio events from CRC computation Jan 7, 2026
@xezon xezon marked this pull request as draft January 7, 2026 18:09
@Caball009 Caball009 marked this pull request as ready for review January 8, 2026 11:13
Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In principle this looks like the right thing to avoid mismatching.

@Caball009 Caball009 linked an issue Jan 8, 2026 that may be closed by this pull request
Copy link

@xezon xezon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@xezon xezon added the Stability Concerns stability of the runtime label Jan 11, 2026
@xezon xezon added this to the Major bug fixes milestone Jan 11, 2026
@xezon xezon added the Bug Something is not working right, typically is user facing label Jan 11, 2026
@Caball009
Copy link
Author

Does this qualify as a bug? I think it was done intentionally, even if we consider it undesirable.

@xezon xezon removed the Bug Something is not working right, typically is user facing label Jan 11, 2026
@xezon
Copy link

xezon commented Jan 11, 2026

I do not know.

@xezon
Copy link

xezon commented Jan 14, 2026

This still has an open TODO item.

@Caball009
Copy link
Author

This still has an open TODO item.

That function isn't used. My hope was that we could signal that it should not be used with GameLogicRandomValue in the future.

@xezon
Copy link

xezon commented Jan 14, 2026

Ok so what are the next steps for this pull?

@Caball009
Copy link
Author

Caball009 commented Jan 16, 2026

#2132 should be merged first, and then this PR should be updated.

@Caball009 Caball009 force-pushed the decouple_scripted_audio_events_from_crc branch from d4fee57 to bfb09c2 Compare January 17, 2026 21:16
@greptile-apps
Copy link

greptile-apps bot commented Jan 17, 2026

Greptile Summary

Decoupled scripted audio events from CRC computation in non-retail mode to prevent multiplayer mismatches caused by script execution failures when using "Local Player" in map scripts.

  • New random functions: Added GetGameLogicRandomValueUnchanged and GetGameLogicRandomValueRealUnchanged that copy the global seed before generating values, leaving the global seed state unchanged in non-retail builds
  • Audio event updates: Replaced GameLogicRandomValue calls with GameLogicRandomValueUnchanged in AudioEventRTS::generateFilename() and AudioEventRTS::generatePlayInfo() for logical audio randomization
  • Early return optimization: Modified AudioManager::addAudioEvent() to allow early returns for logical audio events in non-retail mode, since they no longer affect CRC
  • Retail compatibility: All changes are gated behind RETAIL_COMPATIBLE_CRC preprocessor checks, preserving original behavior in retail builds

The implementation elegantly solves the issue where scripted audio for "Local Player" would cause CRC mismatches - audio events remain synchronized across clients when possible, but failures no longer desync the game state.

Confidence Score: 5/5

  • This PR is safe to merge with minimal risk
  • The changes are well-designed with clear separation of concerns between retail and non-retail builds. The new functions properly copy seed state to avoid side effects, all changes are guarded by preprocessor macros for safety, and the implementation directly addresses the documented issue without introducing new risks.
  • No files require special attention

Important Files Changed

Filename Overview
Core/GameEngine/Source/Common/RandomValue.cpp Added two new "Unchanged" variants of random value functions that use local seed copies to avoid mutating global seed state in non-retail mode
Core/GameEngine/Include/GameLogic/LogicRandomValue.h Added declarations and macros for new "Unchanged" random value functions
Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp Replaced GameLogicRandomValue with GameLogicRandomValueUnchanged in three locations for logical audio event randomization
Core/GameEngine/Source/Common/Audio/GameAudio.cpp Added conditional compilation to decouple logical audio events from CRC in non-retail mode by allowing early returns

Sequence Diagram

sequenceDiagram
    participant Script as Script Engine
    participant AudioMgr as AudioManager
    participant AudioEvent as AudioEventRTS
    participant Random as Random Functions
    participant Seed as Global Seed State
    
    Script->>AudioMgr: addAudioEvent(logicalAudio)
    
    alt RETAIL_COMPATIBLE_CRC enabled
        AudioMgr->>AudioMgr: logicalAudio = TRUE
        Note over AudioMgr: Cannot take early return<br/>Must process to mutate seed
    else Non-retail mode
        AudioMgr->>AudioMgr: logicalAudio = FALSE
        Note over AudioMgr: Can take early return<br/>if notForLocal
    end
    
    AudioMgr->>AudioEvent: generateFilename()
    
    alt Logical Audio
        AudioEvent->>Random: GameLogicRandomValueUnchanged()
        Note over Random: Creates local seed copy
        Random->>Random: randomValue(localSeed)
        Note over Seed: Global seed unchanged
        Random-->>AudioEvent: random value
    else Non-logical Audio
        AudioEvent->>Random: GameAudioRandomValue()
        Random->>Seed: Mutate audio seed
        Random-->>AudioEvent: random value
    end
    
    AudioMgr->>AudioEvent: generatePlayInfo()
    
    alt Logical Audio
        AudioEvent->>Random: GameLogicRandomValueUnchanged()
        Note over Random: Uses local seed copy again
        Random-->>AudioEvent: random value
    else Non-logical Audio
        AudioEvent->>Random: GameAudioRandomValue()
        Random-->>AudioEvent: random value
    end
    
    alt RETAIL_COMPATIBLE_CRC enabled
        alt notForLocal
            AudioMgr->>AudioMgr: Release event
            AudioMgr-->>Script: AHSV_NotForLocal
            Note over AudioMgr: Seed was mutated<br/>CRC stays synced
        end
    else Non-retail mode
        Note over AudioMgr: Already returned early<br/>Seed not mutated
    end
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +243 to +244
if (lo >= hi)
return hi;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: edge case check if (lo >= hi) differs from GetGameLogicRandomValue which checks if (delta == 0) (lines 212-213). When lo == hi, delta is 1 (not 0), so the original proceeds with calculation while this returns early. For true consistency, should check if (delta == 0) or handle lo == hi explicitly

Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngine/Source/Common/RandomValue.cpp
Line: 243:244

Comment:
**style:** edge case check `if (lo >= hi)` differs from `GetGameLogicRandomValue` which checks `if (delta == 0)` (lines 212-213). When `lo == hi`, delta is `1` (not `0`), so the original proceeds with calculation while this returns early. For true consistency, should check `if (delta == 0)` or handle `lo == hi` explicitly

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +344 to +345
if (lo >= hi)
return hi;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: edge case check if (lo >= hi) differs from GetGameLogicRandomValueReal which checks if (delta <= 0.0f) (lines 316-317). When lo == hi, delta is 0.0f, so both would return early. However, when lo > hi, delta is negative - the original returns hi, this also returns hi, but the condition phrasing differs which could confuse readers

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: Core/GameEngine/Source/Common/RandomValue.cpp
Line: 344:345

Comment:
**style:** edge case check `if (lo >= hi)` differs from `GetGameLogicRandomValueReal` which checks `if (delta <= 0.0f)` (lines 316-317). When `lo == hi`, delta is `0.0f`, so both would return early. However, when `lo > hi`, delta is negative - the original returns `hi`, this also returns `hi`, but the condition phrasing differs which could confuse readers

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

@Caball009
Copy link
Author

Updated with the code from #2132.

@Caball009 Caball009 force-pushed the decouple_scripted_audio_events_from_crc branch from 247878f to ce25f44 Compare January 17, 2026 22:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Audio Is audio related Major Severity: Minor < Major < Critical < Blocker NoRetail This fix or change is not applicable with Retail game compatibility Script Is related to Script Engine, SCB Stability Concerns stability of the runtime

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Playing sounds exclusively for "Local Player" can mismatch the game

2 participants