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:
Erik 2026-08-17 18:46:04 +02:00
parent 91c1962b0d
commit 0bb47f2711
8 changed files with 104 additions and 2 deletions

View file

@ -67,6 +67,10 @@ internal sealed record LiveSessionInteractionRuntime(
internal sealed record LiveSessionWorldRuntime(
IDatReaderWriter Dats,
// Logout-audio round (2026-08-17): null only when audio is disabled
// (ACDREAM_NO_AUDIO / init failure) — the reset step and entered-world
// resume both no-op then.
Audio.WorldAudioSessionGate? WorldAudio,
GpuWorldState WorldState,
LiveEntityRuntime LiveEntities,
LiveEntitySessionController EntitySession,
@ -210,7 +214,11 @@ internal sealed class LiveSessionRuntimeFactory
},
SyncToolbar: () => _ui.RetailUi?.SyncToolbarWindowButtons(),
LoadCharacterSettings: _interaction.Settings.LoadCharacterContext,
ArmPlayerModeAutoEntry: _interaction.PlayerModeAutoEntry.Arm),
ArmPlayerModeAutoEntry: _interaction.PlayerModeAutoEntry.Arm,
// Logout-audio round (2026-08-17): reopen the world-audio
// pool the session reset closed (see the reset manifest's
// "world audio" step).
ResumeWorldAudio: () => _world.WorldAudio?.ResumeForWorldEntry()),
Connecting: (host, port, user) =>
_domain.Communication.Chat.OnSystemMessage(
$"connecting to {host}:{port} as {user}",
@ -251,6 +259,7 @@ internal sealed class LiveSessionRuntimeFactory
PlayerPresentation = ResetPlayerPresentation,
TeleportPresentation =
_world.Teleport.ResetGenerationPresentation,
WorldAudio = () => _world.WorldAudio?.SuspendForSessionReset(),
SessionDialogs = () => _ui.RetailUi?.ResetSessionTransientUi(),
SettingsCharacterContext =
_interaction.Settings.RestoreDefaultCharacterContext,