Merge pull request #436 from outis1one/claude/wolf-pair-port-conflict-7nz8qg

Claude/wolf pair port conflict 7nz8qg
This commit is contained in:
Outis
2026-09-03 14:21:58 -04:00
committed by GitHub
+102 -59
View File
@@ -325,7 +325,8 @@ EOF
echo " and Wii U (Cemu) in ES-DE via a second, opt-in 'Alternative emulators' command"
echo " - Expose Wolf's REST API socket to the host (WOLF_SOCKET_PATH + /var/run/wolf mount)"
echo " so './manage.sh controllers' can force distinct pad types per controller slot"
echo " - Force ES-DE's 'Run in background' off durably (mounts + writes esde-settings/es_settings.xml)"
echo " - Mount ~/ES-DE/settings durably + force 'Run in background' off (only once"
echo " GOW's own es_settings.xml template already exists — never pre-created)"
return 0
fi
@@ -1392,48 +1393,56 @@ PS2XMLPY
log_success "PS2: added PCEE2 as the default RetroArch core (custom_systems/es_systems.xml) — ES-DE's"
log_info "own build doesn't ship it yet (added upstream in the not-yet-released 3.5.0)"
# ── ES-DE: force "Run in background" off ────────────────────────────────
# ES-DE's own compiled default for this IS already off (confirmed against
# its real Settings.cpp: mBoolMap["RunInBackground"] = {false, false}) —
# this write exists purely so that default stays durable and explicit
# rather than relying on it never having been toggled. Real, confirmed
# cause of a genuine bug otherwise: with it on, ES-DE keeps running and
# listening to every controller even after a game/emulator has launched
# and taken visual focus a second controller's input can still reach
# ES-DE's own menu in the background and launch (and start playing audio
# for) a completely different game while the first one is still up
# front. ES-DE's own USERGUIDE.md explicitly names this failure mode
# ("make sure that the setting Run in background... is disabled").
# Confirmed live: this affects any system, not just Cemu.
# ── ES-DE: force "Run in background" off (once GOW's own template exists) ──
# GOW's own image bakes a FULL es_settings.xml template (100+ settings —
# confirmed against apps/es-de/build/configs/es/es_settings.xml in the
# games-on-whales/gow repo) that deliberately sets RunInBackground=true
# (NOT ES-DE's own compiled default, which is false — GOW's template is
# what actually causes the real, confirmed cross-system bug this exists
# to fix: with it on, ES-DE keeps listening to every controller even
# after a game/emulator has focus, so a second controller's input can
# reach ES-DE's own menu and launch a different game underneath whatever
# is already running). GOW's own startup.sh copies that template exactly
# ONCE, the very first time the container starts, gated on
# `test -f $ES_CFG_DIR/settings/es_settings.xml` — if a file is already
# sitting there, GOW's own copy is skipped entirely and ES-DE falls back
# to its own vanilla defaults for EVERYTHING ELSE in that file, including
# ROMDirectory (GOW's template sets it to the /ROMs this whole repo
# mounts games at; ES-DE's own default is not that).
#
# es_settings.xml is the user's OWN full settings state (theme, scraper
# prefs, everything) — unlike es_systems.xml this is never a full
# rewrite, only a surgical strip-and-reappend of this one <bool> entry,
# same idempotent pattern as everywhere else in this file. Written as a
# plain top-level element with no <settings> wrapper, matching what
# ES-DE's own Settings::saveFile() actually produces today (confirmed
# against its source — the "wrap everything in <settings>" format is
# loader-side forward-compatibility for a future ES-DE release, not
# what gets written now); either format loads fine either way.
# CONFIRMED LIVE, THE HARD WAY: an earlier version of this exact step
# pre-created a MINIMAL stub file (containing only the RunInBackground
# line) before the container had ever started — GOW's startup.sh saw
# that file "already existing", skipped its own copy, and ES-DE's
# ROMDirectory silently fell back to a default that isn't /ROMs, finding
# zero games on every single system. Root-caused by walking GOW's own
# startup.sh line by line against a real "ES-DE finds no game files"
# report. Never pre-create this file — only ever patch an ALREADY
# existing one (i.e. one GOW's own bootstrap has already populated in
# full, meaning ES-DE has been launched via Moonlight at least once).
mkdir -p "$GAME_STORAGE_DIR/esde-settings"
_ESDE_SETTINGS="$GAME_STORAGE_DIR/esde-settings/es_settings.xml"
backup_if_exists "$_ESDE_SETTINGS"
python3 - "$_ESDE_SETTINGS" << 'ESDESETTINGSPY'
if [ -s "$_ESDE_SETTINGS" ]; then
backup_if_exists "$_ESDE_SETTINGS"
python3 - "$_ESDE_SETTINGS" << 'ESDESETTINGSPY'
import re, sys
path = sys.argv[1]
try:
with open(path) as f:
content = f.read()
except FileNotFoundError:
content = ''
with open(path) as f:
content = f.read()
content = re.sub(r'[ \t]*<bool name="RunInBackground" value="[^"]*"[ \t]*/>[ \t]*\n?', '', content)
line = ' <bool name="RunInBackground" value="false" />\n'
content = (content.rstrip('\n') + '\n' + line) if content.strip() else line
line = '<bool name="RunInBackground" value="false" />\n'
content = content.rstrip('\n') + '\n' + line
with open(path, 'w') as f:
f.write(content)
ESDESETTINGSPY
chown "$ACTUAL_USER:$ACTUAL_USER" "$_ESDE_SETTINGS"
log_success "ES-DE: 'Run in background (while game is launched)' forced off (esde-settings/es_settings.xml)"
chown "$ACTUAL_USER:$ACTUAL_USER" "$_ESDE_SETTINGS"
log_success "ES-DE: 'Run in background (while game is launched)' forced off (esde-settings/es_settings.xml)"
else
log_info "ES-DE: 'Run in background' fix deferred — esde-settings/es_settings.xml doesn't"
log_info "exist yet (GOW writes its real, full template the first time EmulationStation"
log_info "actually launches). Connect to it via Moonlight once, then re-run"
log_info "'sudo ./setup.sh wolf' to apply this fix without touching anything else."
fi
# ── App selection ─────────────────────────────────────────────────────────
echo ""
@@ -3423,8 +3432,24 @@ COMPEOF
# recognize ("oh, that's my gaming PC").
SESSIONS_JSON=$(sudo curl -fsS --unix-socket "$SOCK" http://localhost/api/v1/sessions 2>/dev/null)
# Wolf doesn't dedupe repeated pairings of the same device (confirmed
# live: re-pairing the same PC 6 times over produced 6 separate list
# entries, all with the identical client_id) — and Wolf's own
# get_client_by_id() (config.hpp) resolves by taking the FIRST match
# for a given id anyway, so every duplicate beyond the first is a
# dead ringer with no functional difference. Rather than force a
# choice between 6 identical-outcome options (confirmed live: this
# was a real, confusing thing to be asked to do), the picker only
# ever shows DISTINCT client_ids, first-occurrence order — matching
# what Wolf itself would actually resolve to regardless of which
# duplicate got picked.
CLIENT_IDS=()
while IFS= read -r cid; do CLIENT_IDS+=("$cid"); done < <(echo "$CLIENTS_JSON" | python3 -c "
while IFS= read -r cid; do
for existing in "${CLIENT_IDS[@]}"; do
[ "$existing" = "$cid" ] && continue 2
done
CLIENT_IDS+=("$cid")
done < <(echo "$CLIENTS_JSON" | python3 -c "
import json, sys
for c in json.load(sys.stdin)['clients']:
print(c['client_id'])
@@ -3435,7 +3460,7 @@ for c in json.load(sys.stdin)['clients']:
fi
echo ""
echo "Paired clients (ACTIVE = streaming from this IP right now — the"
echo "Paired devices (ACTIVE = streaming from this IP right now — the"
echo "reliable way to tell which entry is yours. If nothing shows"
echo "ACTIVE, start streaming from the device you want to configure,"
echo "leave it connected, and re-run this in another terminal):"
@@ -3446,21 +3471,21 @@ try:
active = {s['client_id']: s['client_ip'] for s in json.loads(sys.argv[1]).get('sessions', []) if s.get('client_id')}
except Exception:
active = {}
for i, c in enumerate(d['clients']):
ov = c['settings'].get('controllers_override') or []
seen = []
counts = {}
for c in d['clients']:
cid = c['client_id']
counts[cid] = counts.get(cid, 0) + 1
if cid not in seen:
seen.append(cid)
for i, cid in enumerate(seen):
first = next(c for c in d['clients'] if c['client_id'] == cid)
ov = first['settings'].get('controllers_override') or []
tag = 'ACTIVE - streaming from ' + active[cid] if cid in active else 'not currently connected'
print(f\" {i+1}) {cid} [{tag}]\")
dup = f\" (paired {counts[cid]}x)\" if counts[cid] > 1 else ''
print(f\" {i+1}) {cid}{dup} [{tag}]\")
print(f\" current override: {ov if ov else 'none - auto-detect'}\")
" "$SESSIONS_JSON"
DUP_COUNT=$(printf '%s\n' "${CLIENT_IDS[@]}" | sort | uniq -d | wc -l)
if [ "$DUP_COUNT" -gt 0 ]; then
echo ""
echo "Note: some client_ids above are repeated — the same device was"
echo "paired more than once (each pairing gets its own entry, Wolf"
echo "doesn't dedupe). Harmless, but if you want a clean list, forget"
echo "/ re-pair this PC from Moonlight's own settings on the client."
fi
ACTIVE_CLIENT_ID=$(python3 -c "
import json, sys
@@ -4246,10 +4271,12 @@ is actively streaming right now with its real IP address, e.g.
it's used automatically with no prompt. **For the clearest result: start
streaming from the device you want to configure, leave it connected, and
run this command while it's still connected** — then there's no
guessing. It's also normal to see the same client_id listed more than
once (Wolf doesn't dedupe repeated pairings of the same device) — every
duplicate of an active ID is tagged ACTIVE together, so it doesn't matter
which one you'd have picked by hand.
guessing. Wolf doesn't dedupe repeated pairings of the same device — if
you've paired the same PC more than once, this tool collapses those
into a single listed entry tagged e.g. \`(paired 6x)\` instead of forcing
a choice between several options that all resolve identically (confirmed
live: being asked to pick between 6 identical-outcome entries was
genuinely confusing before this).
**It labels controllers "1st"/"2nd"/"3rd"/"4th" — always meaning Wolf's own
"controller 0"/"controller 1"/"controller 2"/"controller 3".** Wolf's own
@@ -4341,15 +4368,31 @@ playing underneath whatever already has focus.
setting. When it's on, ES-DE keeps running and listening to every
controller even after something else has launched and taken visual
focus — ES-DE's own USERGUIDE.md names this exact failure mode directly.
ES-DE's compiled default for this is already off, but nothing durably
enforced that in this setup before now, so it's easy for it to have
gotten toggled on at some point and stick that way.
ES-DE's own *compiled* default for this is off but GOW's own bundled
\`es_settings.xml\` template (baked into \`ghcr.io/games-on-whales/es-de\`)
deliberately sets it **on**, which is the actual, confirmed source of the
default behavior here.
**This installer now forces it off automatically** (\`esde-settings/es_settings.xml\`,
written fresh on every install/reinstall) — no menu digging required. If
you're on an install from before this existed, re-run \`sudo ./setup.sh wolf\`
to pick it up, or set it by hand: **Main Menu → Other Settings → Run in
background (while game is launched) → off**.
**This installer forces it back off** by patching \`esde-settings/es_settings.xml\`
— but only an *already-populated* copy of that file, never by creating one
itself. That distinction matters: GOW's own container startup script only
writes its real template (100+ settings, including where ES-DE looks for
your ROMs) the very first time EmulationStation actually launches, gated
on that file not already existing — pre-creating even a minimal version of
it ourselves, before that first launch, would make GOW skip writing its
own template entirely and leave ES-DE on vanilla upstream defaults for
everything else in that file. **Confirmed live, the hard way:** an earlier
version of this exact fix did precisely that, and ROM discovery broke
completely (every system, not just one) because ES-DE's ROM path fell
back to something other than \`/ROMs\`.
So in practice: if you've already launched EmulationStation via Moonlight
at least once, re-running \`sudo ./setup.sh wolf\` applies this immediately.
**If you haven't yet** (a genuinely fresh install), the installer says so
and skips the patch rather than guessing — connect to EmulationStation
once first, then re-run \`sudo ./setup.sh wolf\`. Setting it by hand works
too, any time: **Main Menu → Other Settings → Run in background (while
game is launched) → off**.
**Why this needed its own mount, not just a one-time write:** \`~/ES-DE\`
(settings, gamelists, scraped artwork, logs) had no bind mount onto the