mirror of
https://github.com/games-on-whales/wolf.git
synced 2026-10-15 13:02:03 +00:00
[GH-ISSUE #460] Multi-user Wine/Proton audio streams jump sinks on 2nd client connection (winepulse.drv default sink routing) #282
Labels
No labels
🧠 1 - 🤖 999
AMD
bug
bug
cannot reproduce
documentation
duplicate
easy
enhancement
enhancement
enhancement
fixed?
fixed?
good first issue
hard
help wanted
Intel
invalid
LXC
medium
moonlight-protocol
Multi GPU
nixos
not a bug
nvidia
pull-request
question
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
games-on-whales/wolf#282
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @dustinblack on GitHub (Jul 21, 2026).
Original GitHub issue: https://github.com/games-on-whales/wolf/issues/460
Problem Description
In multi-user Wolf setups running simultaneous Moonlight client sessions across distinct user profiles, launching a second client session causes active Wine/Proton game audio from the first client to immediately stop playing on Client 1 and jump over to Client 2's audio output.
Steps to Reproduce
mom) and launch a Proton/Wine game (e.g., Meccha Chameleon). Confirm game audio is playing.dad).Root Cause Analysis
virtual_sink_client2and executespactl set-default-sink virtual_sink_client2.winepulse.drvconnects audio playback streams without specifying a target device (pa_stream_connect_playback(stream, dev=NULL)).dev=NULL, PulseAudio attaches the stream to the global Default Sink. When Wolf updates the default sink for Client 2, PulseAudio automatically shifts Client 1's active Wine audio stream over to Client 2's new sink.Verified Fix & Resolution
To isolate audio streams in multi-tenant environments, Wine's
winepulse.drvmust be bound directly to the profile's virtual sink at stream initialization, rather than allowingdev=NULLfallback to the global default sink.1. Wine Registry Device Lock
Adding the following key to the Wine prefix registry (
user.reg):Forces
winepulse.drvto passvirtual_sink_<profile_id>directly intopa_stream_connect_playback().2. Result
Once set, PulseAudio
pactl set-default-sinkcalls for newly connected clients are completely ignored by active Wine streams, guaranteeing 100% audio stream isolation across concurrent multi-user sessions.Suggested Upstream Enhancements
winepulse.drvregistry injection: Automate writingHKCU\Software\Wine\PulseAudio\Deviceduring container prefix initialization for Wine/Proton app runners.PULSE_SINKenvironment variables are inherited cleanly down through sub-shells launched by Steam/Proton wrappers.@ABeltramo commented on GitHub (Jul 21, 2026):
Audio overlaps between sessions have been addressed already in https://github.com/games-on-whales/wolf/pull/347
Can you share more details on exactly how this is failing for you? Which GOW app image are you running? Can you replicate it in Firefox for example? How can we reproduce this?
@dustinblack commented on GitHub (Jul 21, 2026):
Hi @ABeltramo,
Thanks for pointing to PR #347 (
[PULSE_ROUTER]). After further testing across different container images, we can confirm why PR #347 works for native Linux apps (like Firefox) but fails specifically for Wine/Proton games.Here are the details and reproduction steps:
1. Image & App Difference
ghcr.io/games-on-whales/firefox:edge): Does NOT reproduce. Native Linux applications respect `` or anchor their playback streams to specific sinks. PR #347 routes them cleanly and they stay isolated.ghcr.io/games-on-whales/steam:edge): Reproduces consistently with Windows games running under Proton/Wine (e.g., Meccha Chameleon).2. Root Cause & Why PR #347 Is Bypassed by Wine
winepulse.drvStream Connection (dev=NULL): Proton's audio driver (winepulse.drv) ignores `` and connects playback streams without a target device name (pa_stream_connect_playback(stream, dev=NULL)).dev=NULLis passed, PulseAudio dynamically binds the active game audio stream to PulseAudio's Global Default Sink.pactl set-default-sink virtual_sink_client2.dev=NULL) across the daemon to the newly assigned default sink. Even though PR #347 moves the stream initially, PulseAudio's global default sink handler forcibly shifts Client 1's active Proton stream over to Client 2's sink the instant Client 2 connects.3. How to Reproduce
ghcr.io/games-on-whales/steam:edge) and launch any Proton game (e.g., Meccha Chameleon). Confirm game audio plays on Client 1.set-default-sink, Client 1's game audio immediately stops on Client 1 and plays out of Client 2.4. Tested Solution / Workaround
Setting the target device explicitly in Wine's registry prefix (
user.reg):This forces
winepulse.drvto passdev="virtual_sink_<profile_id>"instead ofdev=NULLduringpa_stream_connect_playback(). Once set,winepulse.drvstreams become anchored to that sink and completely ignore globalpactl set-default-sinkchanges.Possible upstream fixes could include injecting
HKCU\Software\Wine\PulseAudio\Deviceduring Wine container prefix initialization, or having[PULSE_ROUTER]re-verify/re-anchordev=NULLstreams when default sink change events fire.@To0wnn commented on GitHub (Oct 5, 2026):
Confirming the registry workaround works for us (Wolf stable 2026-09-29, Proton-GE, 3 players). We set it automatically on every game start in our launcher:
HKCU\Software\Wine\PulseAudio "Device"= the container's$PULSE_SINK(Wolf already passes the session sink in that variable), written into the player's own Wine prefix. The Wine stream then stays on that session's sink when other clients connect. Doing this in the GoW Steam/Lutris images would fix it for everyone.