Skip to content

Commit 8d60896

Browse files
authored
bugfix(logic): Decouple scripted audio events from CRC computation (TheSuperHackers#2075)
1 parent 2029386 commit 8d60896

4 files changed

Lines changed: 77 additions & 4 deletions

File tree

Core/GameEngine/Include/GameLogic/LogicRandomValue.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,15 @@
3333

3434
// do NOT use these functions directly, rather use the macros below
3535
extern Int GetGameLogicRandomValue( int lo, int hi, const char *file, int line );
36+
extern Int GetGameLogicRandomValueUnchanged(int lo, int hi, const char* file, int line);
3637
extern Real GetGameLogicRandomValueReal( Real lo, Real hi, const char *file, int line );
38+
extern Real GetGameLogicRandomValueRealUnchanged(Real lo, Real hi, const char* file, int line);
3739

3840
// use these macros to access the random value functions
3941
#define GameLogicRandomValue( lo, hi ) GetGameLogicRandomValue( lo, hi, __FILE__, __LINE__ )
42+
#define GameLogicRandomValueUnchanged( lo, hi ) GetGameLogicRandomValueUnchanged( lo, hi, __FILE__, __LINE__ )
4043
#define GameLogicRandomValueReal( lo, hi ) GetGameLogicRandomValueReal( lo, hi, __FILE__, __LINE__ )
44+
#define GameLogicRandomValueRealUnchanged( lo, hi ) GetGameLogicRandomValueRealUnchanged( lo, hi, __FILE__, __LINE__ )
4145

4246
//--------------------------------------------------------------------------------------------------------------
4347
class CColorAlphaDialog;

Core/GameEngine/Source/Common/Audio/AudioEventRTS.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ void AudioEventRTS::generateFilename( void )
340340
{
341341
if (m_isLogicalAudio)
342342
{
343-
which = GameLogicRandomValue(0, m_eventInfo->m_sounds.size() - 1);
343+
which = GameLogicRandomValueUnchanged(0, m_eventInfo->m_sounds.size() - 1);
344344
}
345345
else
346346
{
@@ -388,7 +388,7 @@ void AudioEventRTS::generatePlayInfo( void )
388388
// needs to be logic because it needs to be the same on all systems.
389389
Int attackToPlay;
390390
if (m_isLogicalAudio) {
391-
attackToPlay = GameLogicRandomValue(0, attackSize - 1);
391+
attackToPlay = GameLogicRandomValueUnchanged(0, attackSize - 1);
392392
} else {
393393
attackToPlay = GameAudioRandomValue(0, attackSize - 1);
394394
}
@@ -406,7 +406,7 @@ void AudioEventRTS::generatePlayInfo( void )
406406
// needs to be logic because it needs to be the same on all systems.
407407
Int decayToPlay;
408408
if (m_isLogicalAudio) {
409-
decayToPlay = GameLogicRandomValue(0, decaySize - 1);
409+
decayToPlay = GameLogicRandomValueUnchanged(0, decaySize - 1);
410410
} else {
411411
decayToPlay = GameAudioRandomValue(0, decaySize - 1);
412412
}

Core/GameEngine/Source/Common/Audio/GameAudio.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -444,8 +444,13 @@ AudioHandle AudioManager::addAudioEvent(const AudioEventRTS *eventToAdd)
444444
}
445445

446446
// TheSuperHackers @info Scripted audio events are logical, i.e. synchronized across clients.
447-
// This early return cannot be taken for such audio events as it skips code that changes the logical game seed values.
447+
// In retail mode this early return cannot be taken for such audio events as it skips code that changes the logical game seed values.
448+
// In non-retail mode logical audio events are decoupled from the CRC computation, so this early return is allowed.
449+
#if RETAIL_COMPATIBLE_CRC
448450
const Bool logicalAudio = eventToAdd->getIsLogicalAudio();
451+
#else
452+
const Bool logicalAudio = FALSE;
453+
#endif
449454
const Bool notForLocal = !eventToAdd->getUninterruptable() && !shouldPlayLocally(eventToAdd);
450455

451456
if (!logicalAudio && notForLocal)
@@ -467,11 +472,13 @@ AudioHandle AudioManager::addAudioEvent(const AudioEventRTS *eventToAdd)
467472
}
468473
}
469474

475+
#if RETAIL_COMPATIBLE_CRC
470476
if (notForLocal)
471477
{
472478
releaseAudioEventRTS(audioEvent);
473479
return AHSV_NotForLocal;
474480
}
481+
#endif
475482

476483
// cull muted audio
477484
if (audioEvent->getVolume() < m_audioSettings->m_minVolume) {

Core/GameEngine/Source/Common/RandomValue.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,37 @@ DEBUG_LOG(( "%d: GetGameLogicRandomValue = %d (%d - %d), %s line %d",
228228
return rval;
229229
}
230230

231+
//
232+
// TheSuperHackers @info This function does not change the seed values with retail compatibility disabled.
233+
// Consecutive calls always return the same value for the same combination of min / max values, assuming the seed values haven't changed in between.
234+
// The intended use case for this function are randomized values that are desirable to be synchronized across clients,
235+
// but should not result in a mismatch if they aren't synchronized; e.g. for scripted audio events.
236+
//
237+
Int GetGameLogicRandomValueUnchanged( int lo, int hi, const char *file, int line )
238+
{
239+
#if RETAIL_COMPATIBLE_CRC
240+
return GetGameLogicRandomValue(lo, hi, file, line);
241+
#endif
242+
243+
const UnsignedInt delta = hi - lo + 1;
244+
if (delta == 0)
245+
return hi;
246+
247+
UnsignedInt seed[ARRAY_SIZE(theGameLogicSeed)];
248+
memcpy(&seed[0], &theGameLogicSeed[0], sizeof(seed));
249+
250+
const Int rval = ((Int)(randomValue(seed) % delta)) + lo;
251+
252+
DEBUG_ASSERTCRASH(rval >= lo && rval <= hi, ("Bad random val"));
253+
254+
#ifdef DEBUG_RANDOM_LOGIC
255+
DEBUG_LOG(( "%d: GetGameLogicRandomValueUnchanged = %d (%d - %d), %s line %d",
256+
TheGameLogic->getFrame(), rval, lo, hi, file, line ));
257+
#endif
258+
259+
return rval;
260+
}
261+
231262
//
232263
// Integer random value
233264
//
@@ -298,6 +329,37 @@ DEBUG_LOG(( "%d: GetGameLogicRandomValueReal = %f, %s line %d",
298329
return rval;
299330
}
300331

332+
//
333+
// TheSuperHackers @info This function does not change the seed values with retail compatibility disabled.
334+
// Consecutive calls always return the same value for the same combination of min / max values, assuming the seed values haven't changed in between.
335+
// The intended use case for this function are randomized values that are desirable to be synchronized across clients,
336+
// but should not result in a mismatch if they aren't synchronized; e.g. for scripted audio events.
337+
//
338+
Real GetGameLogicRandomValueRealUnchanged( Real lo, Real hi, const char *file, int line )
339+
{
340+
#if RETAIL_COMPATIBLE_CRC
341+
return GetGameLogicRandomValueReal(lo, hi, file, line);
342+
#endif
343+
344+
const Real delta = hi - lo;
345+
if (delta <= 0.0f)
346+
return hi;
347+
348+
UnsignedInt seed[ARRAY_SIZE(theGameLogicSeed)];
349+
memcpy(&seed[0], &theGameLogicSeed[0], sizeof(seed));
350+
351+
const Real rval = ((Real)(randomValue(seed)) * theMultFactor) * delta + lo;
352+
353+
DEBUG_ASSERTCRASH(rval >= lo && rval <= hi, ("Bad random val"));
354+
355+
#ifdef DEBUG_RANDOM_LOGIC
356+
DEBUG_LOG(( "%d: GetGameLogicRandomValueRealUnchanged = %f, %s line %d",
357+
TheGameLogic->getFrame(), rval, file, line ));
358+
#endif
359+
360+
return rval;
361+
}
362+
301363
//
302364
// Real valued random value
303365
//

0 commit comments

Comments
 (0)