fix #417: world ambience kept playing at character select after the in-world logoff
The character-session reset manifest had no audio step: retail's logoff destroys the world's sound sources with the world, but our OpenAL world pool and ambient scheduler are process-lifetime — the continuous ambient beds played on at character select and the scheduler kept RE-FIRING deadlines against the stale listener (Suspend/StopAll had zero callers; WorldGenerationQuiescence only cycles around teleport-style generation replaces). New WorldAudioSessionGate: the reset manifest's 'world audio' step stops all sixteen world-pool voices (SuspendWorldAudio) and drops every ambient deadline (StopAll); the pool reopens at the entered-world edge through the new default-null LiveSessionEnteredWorldBindings.ResumeWorldAudio binding, invoked first in ApplyEnteredWorld. The ambient soundscape needs no explicit resume — the next objcell observation rebuilds it exactly as a cell change always did. Covers logout, reconnect, and full stop uniformly. UI-pool sounds (interface bank, portal cues) untouched by design. App tests 5568/3 skips, Runtime 1756/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
91c1962b0d
commit
0bb47f2711
8 changed files with 104 additions and 2 deletions
39
src/AcDream.App/Audio/WorldAudioSessionGate.cs
Normal file
39
src/AcDream.App/Audio/WorldAudioSessionGate.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
namespace AcDream.App.Audio;
|
||||
|
||||
/// <summary>
|
||||
/// Logout-audio round (2026-08-17): the character-session reset's world-audio
|
||||
/// teardown. Retail's logoff destroys every world sound source WITH the world
|
||||
/// (<c>CPlayerSystem::ExecuteLogOff @ 0x0055D780</c> world teardown — the
|
||||
/// DirectSound buffers die with their owners), so the character-select screen
|
||||
/// is silent. acdream's OpenAL voices and the ambient scheduler are
|
||||
/// process-lifetime, so the session reset must stop them explicitly:
|
||||
/// <see cref="SuspendForSessionReset"/> stops all sixteen world-pool voices
|
||||
/// (the continuous ambient beds included) and drops every ambient deadline so
|
||||
/// nothing re-fires against the stale listener at character select. The pool
|
||||
/// re-opens at the next entered-world edge
|
||||
/// (<c>LiveSessionEnteredWorldBindings.ResumeWorldAudio</c>); the ambient
|
||||
/// soundscape needs no explicit resume — the next
|
||||
/// <c>AmbientSoundController.ObserveListener</c> objcell change rebuilds it,
|
||||
/// exactly as a cell change always does.
|
||||
/// </summary>
|
||||
public sealed class WorldAudioSessionGate
|
||||
{
|
||||
private readonly OpenAlAudioEngine _engine;
|
||||
private readonly AmbientSoundController? _ambient;
|
||||
|
||||
public WorldAudioSessionGate(
|
||||
OpenAlAudioEngine engine,
|
||||
AmbientSoundController? ambient)
|
||||
{
|
||||
_engine = engine ?? throw new ArgumentNullException(nameof(engine));
|
||||
_ambient = ambient;
|
||||
}
|
||||
|
||||
public void SuspendForSessionReset()
|
||||
{
|
||||
_engine.SuspendWorldAudio();
|
||||
_ambient?.StopAll();
|
||||
}
|
||||
|
||||
public void ResumeForWorldEntry() => _engine.ResumeWorldAudio();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue