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 " 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 " - 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 " 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 return 0
fi 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_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)" 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: force "Run in background" off (once GOW's own template exists) ──
# ES-DE's own compiled default for this IS already off (confirmed against # GOW's own image bakes a FULL es_settings.xml template (100+ settings —
# its real Settings.cpp: mBoolMap["RunInBackground"] = {false, false}) — # confirmed against apps/es-de/build/configs/es/es_settings.xml in the
# this write exists purely so that default stays durable and explicit # games-on-whales/gow repo) that deliberately sets RunInBackground=true
# rather than relying on it never having been toggled. Real, confirmed # (NOT ES-DE's own compiled default, which is false — GOW's template is
# cause of a genuine bug otherwise: with it on, ES-DE keeps running and # what actually causes the real, confirmed cross-system bug this exists
# listening to every controller even after a game/emulator has launched # to fix: with it on, ES-DE keeps listening to every controller even
# and taken visual focus a second controller's input can still reach # after a game/emulator has focus, so a second controller's input can
# ES-DE's own menu in the background and launch (and start playing audio # reach ES-DE's own menu and launch a different game underneath whatever
# for) a completely different game while the first one is still up # is already running). GOW's own startup.sh copies that template exactly
# front. ES-DE's own USERGUIDE.md explicitly names this failure mode # ONCE, the very first time the container starts, gated on
# ("make sure that the setting Run in background... is disabled"). # `test -f $ES_CFG_DIR/settings/es_settings.xml` — if a file is already
# Confirmed live: this affects any system, not just Cemu. # 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 # CONFIRMED LIVE, THE HARD WAY: an earlier version of this exact step
# prefs, everything) — unlike es_systems.xml this is never a full # pre-created a MINIMAL stub file (containing only the RunInBackground
# rewrite, only a surgical strip-and-reappend of this one <bool> entry, # line) before the container had ever started — GOW's startup.sh saw
# same idempotent pattern as everywhere else in this file. Written as a # that file "already existing", skipped its own copy, and ES-DE's
# plain top-level element with no <settings> wrapper, matching what # ROMDirectory silently fell back to a default that isn't /ROMs, finding
# ES-DE's own Settings::saveFile() actually produces today (confirmed # zero games on every single system. Root-caused by walking GOW's own
# against its source — the "wrap everything in <settings>" format is # startup.sh line by line against a real "ES-DE finds no game files"
# loader-side forward-compatibility for a future ES-DE release, not # report. Never pre-create this file — only ever patch an ALREADY
# what gets written now); either format loads fine either way. # 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" mkdir -p "$GAME_STORAGE_DIR/esde-settings"
_ESDE_SETTINGS="$GAME_STORAGE_DIR/esde-settings/es_settings.xml" _ESDE_SETTINGS="$GAME_STORAGE_DIR/esde-settings/es_settings.xml"
backup_if_exists "$_ESDE_SETTINGS" if [ -s "$_ESDE_SETTINGS" ]; then
python3 - "$_ESDE_SETTINGS" << 'ESDESETTINGSPY' backup_if_exists "$_ESDE_SETTINGS"
python3 - "$_ESDE_SETTINGS" << 'ESDESETTINGSPY'
import re, sys import re, sys
path = sys.argv[1] path = sys.argv[1]
try: with open(path) as f:
with open(path) as f: content = f.read()
content = f.read()
except FileNotFoundError:
content = ''
content = re.sub(r'[ \t]*<bool name="RunInBackground" value="[^"]*"[ \t]*/>[ \t]*\n?', '', content) content = re.sub(r'[ \t]*<bool name="RunInBackground" value="[^"]*"[ \t]*/>[ \t]*\n?', '', content)
line = ' <bool name="RunInBackground" value="false" />\n' line = '<bool name="RunInBackground" value="false" />\n'
content = (content.rstrip('\n') + '\n' + line) if content.strip() else line content = content.rstrip('\n') + '\n' + line
with open(path, 'w') as f: with open(path, 'w') as f:
f.write(content) f.write(content)
ESDESETTINGSPY ESDESETTINGSPY
chown "$ACTUAL_USER:$ACTUAL_USER" "$_ESDE_SETTINGS" 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)" 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 ───────────────────────────────────────────────────────── # ── App selection ─────────────────────────────────────────────────────────
echo "" echo ""
@@ -3423,8 +3432,24 @@ COMPEOF
# recognize ("oh, that's my gaming PC"). # recognize ("oh, that's my gaming PC").
SESSIONS_JSON=$(sudo curl -fsS --unix-socket "$SOCK" http://localhost/api/v1/sessions 2>/dev/null) 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=() 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 import json, sys
for c in json.load(sys.stdin)['clients']: for c in json.load(sys.stdin)['clients']:
print(c['client_id']) print(c['client_id'])
@@ -3435,7 +3460,7 @@ for c in json.load(sys.stdin)['clients']:
fi fi
echo "" 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 "reliable way to tell which entry is yours. If nothing shows"
echo "ACTIVE, start streaming from the device you want to configure," echo "ACTIVE, start streaming from the device you want to configure,"
echo "leave it connected, and re-run this in another terminal):" 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')} active = {s['client_id']: s['client_ip'] for s in json.loads(sys.argv[1]).get('sessions', []) if s.get('client_id')}
except Exception: except Exception:
active = {} active = {}
for i, c in enumerate(d['clients']): seen = []
ov = c['settings'].get('controllers_override') or [] counts = {}
for c in d['clients']:
cid = c['client_id'] 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' 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'}\") print(f\" current override: {ov if ov else 'none - auto-detect'}\")
" "$SESSIONS_JSON" " "$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 " ACTIVE_CLIENT_ID=$(python3 -c "
import json, sys 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 it's used automatically with no prompt. **For the clearest result: start
streaming from the device you want to configure, leave it connected, and streaming from the device you want to configure, leave it connected, and
run this command while it's still connected** — then there's no 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 guessing. Wolf doesn't dedupe repeated pairings of the same device — if
once (Wolf doesn't dedupe repeated pairings of the same device) — every you've paired the same PC more than once, this tool collapses those
duplicate of an active ID is tagged ACTIVE together, so it doesn't matter into a single listed entry tagged e.g. \`(paired 6x)\` instead of forcing
which one you'd have picked by hand. 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 **It labels controllers "1st"/"2nd"/"3rd"/"4th" — always meaning Wolf's own
"controller 0"/"controller 1"/"controller 2"/"controller 3".** 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 setting. When it's on, ES-DE keeps running and listening to every
controller even after something else has launched and taken visual controller even after something else has launched and taken visual
focus — ES-DE's own USERGUIDE.md names this exact failure mode directly. 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 ES-DE's own *compiled* default for this is off but GOW's own bundled
enforced that in this setup before now, so it's easy for it to have \`es_settings.xml\` template (baked into \`ghcr.io/games-on-whales/es-de\`)
gotten toggled on at some point and stick that way. 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\`, **This installer forces it back off** by patching \`esde-settings/es_settings.xml\`
written fresh on every install/reinstall) — no menu digging required. If — but only an *already-populated* copy of that file, never by creating one
you're on an install from before this existed, re-run \`sudo ./setup.sh wolf\` itself. That distinction matters: GOW's own container startup script only
to pick it up, or set it by hand: **Main Menu → Other Settings → Run in writes its real template (100+ settings, including where ES-DE looks for
background (while game is launched) → off**. 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\` **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 (settings, gamelists, scraped artwork, logs) had no bind mount onto the