Merge pull request #435 from outis1one/claude/wolf-pair-port-conflict-7nz8qg
Claude/wolf pair port conflict 7nz8qg
This commit is contained in:
+249
-8
@@ -325,6 +325,7 @@ 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)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
@@ -1391,6 +1392,49 @@ 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_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.
|
||||
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'
|
||||
import re, sys
|
||||
path = sys.argv[1]
|
||||
try:
|
||||
with open(path) as f:
|
||||
content = f.read()
|
||||
except FileNotFoundError:
|
||||
content = ''
|
||||
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
|
||||
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)"
|
||||
|
||||
# ── App selection ─────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "═══════════════════════════════════════════════════════"
|
||||
@@ -1941,7 +1985,23 @@ CATALOG = {
|
||||
f'{games}/retroarch:/home/retro/.config/retroarch:rw',
|
||||
f'{games}/emulators:/mnt/games/emulators:rw',
|
||||
f'{games}/emulators:/home/retro/Applications:rw',
|
||||
f'{games}/esde-custom-systems:/home/retro/ES-DE/custom_systems:rw'],
|
||||
f'{games}/esde-custom-systems:/home/retro/ES-DE/custom_systems:rw',
|
||||
# ~/ES-DE (settings, gamelists, downloaded_media, logs) has
|
||||
# NO mount at all otherwise (confirmed against ES-DE's own
|
||||
# source and GOW's es-de startup.sh: getAppDataDirectory()
|
||||
# is a plain $HOME/ES-DE, no XDG redirect) — Wolf normally
|
||||
# reuses the same app container across sessions rather than
|
||||
# recreating it each time (confirmed against Wolf's own
|
||||
# docker.cpp: it only removes the container on session end
|
||||
# if WOLF_STOP_CONTAINER_ON_EXIT=TRUE, which this repo never
|
||||
# sets), so this doesn't get wiped every reconnect — but it
|
||||
# IS lost on any real reinstall/container recreate, unlike
|
||||
# everything else here which lives on the game drive. Only
|
||||
# mounting settings/ specifically (not the whole ~/ES-DE
|
||||
# tree) keeps this additive and non-breaking alongside the
|
||||
# existing custom_systems mount above — gamelists/scraped
|
||||
# media durability is a separate, not-yet-done improvement.
|
||||
f'{games}/esde-settings:/home/retro/ES-DE/settings:rw'],
|
||||
env=ESDE_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host',
|
||||
ulimits=[], privileged=False,
|
||||
devices=['/dev/uinput:/dev/uinput'],
|
||||
@@ -3415,14 +3475,17 @@ print(next(iter(ids)) if len(ids) == 1 else '')
|
||||
if [ -n "$ACTIVE_CLIENT_ID" ]; then
|
||||
CLIENT_ID="$ACTIVE_CLIENT_ID"
|
||||
echo ""
|
||||
echo "Exactly one client is actively streaming right now — using that one."
|
||||
echo "Exactly one Moonlight session (device/TV) is actively streaming right"
|
||||
echo "now — using that one. (This is about which SCREEN/DEVICE you're"
|
||||
echo "configuring, not how many controllers are plugged into it — that's"
|
||||
echo "the next question.)"
|
||||
elif [ "${#CLIENT_IDS[@]}" -eq 1 ]; then
|
||||
CLIENT_ID="${CLIENT_IDS[0]}"
|
||||
echo ""
|
||||
echo "Only one paired client — using it."
|
||||
echo "Only one paired Moonlight device — using it."
|
||||
else
|
||||
echo ""
|
||||
read -r -p "Which client [1-${#CLIENT_IDS[@]}]: " PICK
|
||||
read -r -p "Which Moonlight session/device [1-${#CLIENT_IDS[@]}]: " PICK
|
||||
if ! [[ "$PICK" =~ ^[0-9]+$ ]] || [ "$PICK" -lt 1 ] || [ "$PICK" -gt "${#CLIENT_IDS[@]}" ]; then
|
||||
echo "Invalid selection."
|
||||
exit 1
|
||||
@@ -3430,8 +3493,64 @@ print(next(iter(ids)) if len(ids) == 1 else '')
|
||||
CLIENT_ID="${CLIENT_IDS[$((PICK-1))]}"
|
||||
fi
|
||||
|
||||
# Wolf's own logs record "Creating <TYPE> joypad for controller <N>"
|
||||
# every time it creates a virtual pad (confirmed live against real
|
||||
# log output) — polling this gives real, human-readable context per
|
||||
# slot instead of asking the user to guess or watch logs by hand
|
||||
# themselves. Wolf's own wording is 0-indexed ("controller 0",
|
||||
# "controller 1"); every prompt in this tool is deliberately
|
||||
# 1-indexed instead ("1st controller", "2nd controller"...) —
|
||||
# confirmed live that mixing the two ("exactly one CLIENT is
|
||||
# active" read as "exactly one CONTROLLER" right above a
|
||||
# 0-indexed-sounding prompt) is a real, easy misread. "1st
|
||||
# controller" here always means Wolf's "controller 0", "2nd" means
|
||||
# "controller 1", and so on — stated explicitly at every prompt so
|
||||
# the two numbering schemes never have to be reconciled in your head.
|
||||
#
|
||||
# NOTE: this only tells you what TYPE Wolf most recently created
|
||||
# for a given slot NUMBER — it can't tell you which PHYSICAL
|
||||
# controller that was, and it can't let you reassign a physical
|
||||
# controller to a different slot number. Which controller becomes
|
||||
# slot 0 vs. slot 1 is decided entirely by Moonlight (the client),
|
||||
# upstream of Wolf, based on connection order — not something this
|
||||
# override (or Wolf's API at all) can control.
|
||||
_ordsuffix() {
|
||||
case "$1" in
|
||||
1) echo "st" ;;
|
||||
2) echo "nd" ;;
|
||||
3) echo "rd" ;;
|
||||
*) echo "th" ;;
|
||||
esac
|
||||
}
|
||||
echo ""
|
||||
read -r -p "How many controller slots to force a type for [2]: " NSLOTS
|
||||
echo "Checking Wolf's logs for controllers it has already created a pad for..."
|
||||
declare -A LAST_SEEN_TYPE
|
||||
while IFS=$'\t' read -r cnum ctype; do
|
||||
[ -n "$cnum" ] && LAST_SEEN_TYPE["$cnum"]="$ctype"
|
||||
done < <(docker compose logs wolf 2>/dev/null | python3 -c "
|
||||
import re, sys
|
||||
for line in sys.stdin:
|
||||
m = re.search(r'Creating (\w+) joypad for controller (\d+)', line)
|
||||
if m:
|
||||
print(f'{m.group(2)}\t{m.group(1).upper()}')
|
||||
")
|
||||
if [ "${#LAST_SEEN_TYPE[@]}" -gt 0 ]; then
|
||||
echo "Last type Wolf created, per controller (most recent reconnect wins):"
|
||||
for cnum in $(printf '%s\n' "${!LAST_SEEN_TYPE[@]}" | sort -n); do
|
||||
_n=$((cnum + 1))
|
||||
echo " Your ${_n}$(_ordsuffix "$_n") controller (Wolf calls it \"controller $cnum\"): last seen as ${LAST_SEEN_TYPE[$cnum]}"
|
||||
done
|
||||
else
|
||||
echo "No controllers found in Wolf's logs yet — connect them and start a"
|
||||
echo "stream first if you want this list populated (not required, you can"
|
||||
echo "still answer blind below)."
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Now, separately: how many physical GAMEPADS/CONTROLLERS do you have"
|
||||
echo "connected to that device right now (this tool can't detect that —"
|
||||
echo "count your own controllers)?"
|
||||
read -r -p "Number of controllers [2]: " NSLOTS
|
||||
NSLOTS="${NSLOTS:-2}"
|
||||
if ! [[ "$NSLOTS" =~ ^[0-9]+$ ]] || [ "$NSLOTS" -lt 1 ]; then
|
||||
echo "Invalid number."
|
||||
@@ -3440,8 +3559,16 @@ print(next(iter(ids)) if len(ids) == 1 else '')
|
||||
|
||||
OVERRIDE_TYPES=()
|
||||
for i in $(seq 1 "$NSLOTS"); do
|
||||
read -r -p " Slot $i type (AUTO/XBOX/PS/NINTENDO) [AUTO]: " T
|
||||
T="${T:-AUTO}"
|
||||
cnum=$((i - 1))
|
||||
SEEN="${LAST_SEEN_TYPE[$cnum]:-}"
|
||||
DEFAULT="${SEEN:-AUTO}"
|
||||
SUF="$(_ordsuffix "$i")"
|
||||
if [ -n "$SEEN" ]; then
|
||||
read -r -p " Your ${i}${SUF} controller (controller $cnum, last seen as $SEEN) — force to which type? (AUTO/XBOX/PS/NINTENDO) [$DEFAULT]: " T
|
||||
else
|
||||
read -r -p " Your ${i}${SUF} controller (controller $cnum, not seen yet) — force to which type? (AUTO/XBOX/PS/NINTENDO) [AUTO]: " T
|
||||
fi
|
||||
T="${T:-$DEFAULT}"
|
||||
T="$(echo "$T" | tr '[:lower:]' '[:upper:]')"
|
||||
case "$T" in
|
||||
AUTO|XBOX|PS|NINTENDO) ;;
|
||||
@@ -3603,7 +3730,23 @@ CATALOG = {
|
||||
f'{games}/retroarch:/home/retro/.config/retroarch:rw',
|
||||
f'{games}/emulators:/mnt/games/emulators:rw',
|
||||
f'{games}/emulators:/home/retro/Applications:rw',
|
||||
f'{games}/esde-custom-systems:/home/retro/ES-DE/custom_systems:rw'],
|
||||
f'{games}/esde-custom-systems:/home/retro/ES-DE/custom_systems:rw',
|
||||
# ~/ES-DE (settings, gamelists, downloaded_media, logs) has
|
||||
# NO mount at all otherwise (confirmed against ES-DE's own
|
||||
# source and GOW's es-de startup.sh: getAppDataDirectory()
|
||||
# is a plain $HOME/ES-DE, no XDG redirect) — Wolf normally
|
||||
# reuses the same app container across sessions rather than
|
||||
# recreating it each time (confirmed against Wolf's own
|
||||
# docker.cpp: it only removes the container on session end
|
||||
# if WOLF_STOP_CONTAINER_ON_EXIT=TRUE, which this repo never
|
||||
# sets), so this doesn't get wiped every reconnect — but it
|
||||
# IS lost on any real reinstall/container recreate, unlike
|
||||
# everything else here which lives on the game drive. Only
|
||||
# mounting settings/ specifically (not the whole ~/ES-DE
|
||||
# tree) keeps this additive and non-breaking alongside the
|
||||
# existing custom_systems mount above — gamelists/scraped
|
||||
# media durability is a separate, not-yet-done improvement.
|
||||
f'{games}/esde-settings:/home/retro/ES-DE/settings:rw'],
|
||||
env=ESDE_ENV, cap_add=STD_CAP, security_opt=[], ipc_mode='host',
|
||||
ulimits=[], privileged=False,
|
||||
devices=['/dev/uinput:/dev/uinput'],
|
||||
@@ -4108,6 +4251,31 @@ 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.
|
||||
|
||||
**It labels controllers "1st"/"2nd"/"3rd"/"4th" — always meaning Wolf's own
|
||||
"controller 0"/"controller 1"/"controller 2"/"controller 3".** Wolf's own
|
||||
log wording is 0-indexed; this tool is deliberately 1-indexed throughout
|
||||
so the two numbering schemes never have to be reconciled in your head
|
||||
(confirmed live that mixing them — reading "exactly one client is
|
||||
active" as "exactly one controller" right above a 0-indexed-sounding
|
||||
prompt — is a real, easy misread).
|
||||
|
||||
Before asking anything, it polls \`docker compose logs wolf\` for every
|
||||
\`Creating <TYPE> joypad for controller <N>\` line Wolf has ever logged
|
||||
(most recent per slot wins) and shows you what it last saw, e.g.:
|
||||
\`\`\`
|
||||
Your 1st controller (Wolf calls it "controller 0"): last seen as NINTENDO
|
||||
Your 2nd controller (Wolf calls it "controller 1"): last seen as XBOX
|
||||
\`\`\`
|
||||
— then uses that as the suggested default at each prompt, so if nothing's
|
||||
changed you can just hit Enter through them. **This can't tell you which
|
||||
physical controller became which slot, and it can't let you reassign
|
||||
one** — which controller becomes slot 0 vs. slot 1 is decided entirely by
|
||||
Moonlight (the client), based on connection order, upstream of Wolf and
|
||||
this tool alike. If you need to know that for certain, connect your
|
||||
controllers one at a time while following the log live
|
||||
(\`docker compose logs -f wolf | grep -i controller\`) — you'll see
|
||||
exactly which slot number each one becomes as it connects.
|
||||
|
||||
Then it asks how many controllers to configure and what type to force
|
||||
each one to. There are only **3 concrete types** (confirmed against Wolf's own
|
||||
virtual-pad source, \`inputtino\`) — not one per real controller brand:
|
||||
@@ -4163,6 +4331,41 @@ regardless of which type it's sharing a GUID with. This is a reasoned
|
||||
workaround, not one confirmed working live — if it doesn't pan out,
|
||||
that's useful to know.
|
||||
|
||||
## ES-DE launches a second game while one is already running
|
||||
**Symptom** (confirmed live, not Cemu-specific — happens on any system):
|
||||
a second controller's input reaches ES-DE's own menu in the background
|
||||
and launches an entirely different game — you'll hear/see it start
|
||||
playing underneath whatever already has focus.
|
||||
|
||||
**Cause:** ES-DE's own **"Run in background (while game is launched)"**
|
||||
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.
|
||||
|
||||
**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**.
|
||||
|
||||
**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
|
||||
game drive at all before this — confirmed against ES-DE's own source
|
||||
(\`getAppDataDirectory()\` is a plain \`$HOME/ES-DE\`, no XDG redirect) and
|
||||
GOW's own \`es-de\` startup script. In normal day-to-day use this mostly
|
||||
didn't matter, since Wolf reuses the same app container across sessions
|
||||
rather than recreating it each time (confirmed against Wolf's own
|
||||
\`docker.cpp\`: the container is only removed on session end if
|
||||
\`WOLF_STOP_CONTAINER_ON_EXIT=TRUE\`, which this repo never sets) — but it
|
||||
meant this setting, and everything else under \`~/ES-DE\`, wasn't safe
|
||||
across an actual reinstall or container recreate the way roms/saves/BIOS
|
||||
already are. Only \`settings/\` is mounted now (\`esde-settings/\`, additive,
|
||||
doesn't touch the existing \`custom_systems\` mount) — gamelists and
|
||||
scraped media durability is a related gap, not yet fixed.
|
||||
|
||||
## EA games (Battlefront II 2017, etc.)
|
||||
EA titles require GE-Proton and the EA App to be installed inside the Wine
|
||||
prefix. The EA App handles authentication — without it, the game launches
|
||||
@@ -4391,6 +4594,44 @@ time Cemu launches from ES-DE too.
|
||||
characters?** That's not a Cemu-specific bug — see
|
||||
"Multiple controllers" above (\`./manage.sh controllers\`).
|
||||
|
||||
**If Input Settings' Save button is cut off the bottom of the screen**
|
||||
(confirmed live: happens even maximized, at higher resolutions, and
|
||||
closing the window doesn't save — Cemu's Input Settings dialog is a
|
||||
fixed size, not resizable, and can genuinely be taller than the session's
|
||||
viewport) — try moving the dialog first: hold **Alt** and **left-click-drag
|
||||
anywhere inside it** (not just the titlebar) to reposition it upward, or
|
||||
use xfwm4's own **Alt+F7** (grab-to-move, drop with click or Enter) if
|
||||
that doesn't work. Either is a plain X11/XFCE window-manager trick, not
|
||||
Cemu-specific, and usually the fastest fix.
|
||||
|
||||
**If that still doesn't work, or you'd rather skip the GUI entirely: edit
|
||||
the controller profile file directly.** Cemu's per-slot controller config
|
||||
isn't inside the Docker container at all — it's plain XML on the game
|
||||
drive (Desktop and ES-DE both mount \`~/.config\` from the same place):
|
||||
\`\`\`bash
|
||||
GAME_DIR=\$(grep '^GAME_STORAGE_DIR=' $WOLF_DIR/.env | cut -d= -f2-)
|
||||
ls "\$GAME_DIR/retro-home/Cemu/controllerProfiles/"
|
||||
\`\`\`
|
||||
File naming is 0-indexed the same way Wolf's own controller numbering is
|
||||
(yet another place this comes up) — \`controller0.xml\` is Cemu's
|
||||
**Controller 1**, \`controller1.xml\` is **Controller 2**, and so on.
|
||||
Each holds one or more \`<controller>\` blocks (one per SDL device that's
|
||||
ever been assigned to that slot, identified by its own \`<uuid>\`/
|
||||
\`<display_name>\`) with a \`<mappings>\` list of \`<mapping>\`/\`<button>\`
|
||||
pairs. **Confirmed against real captured profiles from different
|
||||
controller brands: these values are universal SDL_CONTROLLER_BUTTON_*
|
||||
semantics, not raw per-device button indices** — so a complete, working
|
||||
\`<mappings>\` block can be copied verbatim from one \`<controller>\` entry
|
||||
into a different device's empty one in the same file (e.g. a
|
||||
newly-selected device whose Save got cut off, leaving it with
|
||||
\`<mappings />\` and nothing bound) without touching Cemu's GUI at all. If
|
||||
a slot ends up with more than one \`<controller>\` block and only one
|
||||
device is actually in use, deleting the stale block(s) removes any
|
||||
ambiguity about which one Cemu picks. Back up the file first
|
||||
(\`cp controllerN.xml controllerN.xml.bak\`) before hand-editing; no
|
||||
\`sudo\` needed, these files are written world-writable by Cemu itself.
|
||||
Close Cemu completely and relaunch for a hand-edited profile to take effect.
|
||||
|
||||
ROMs are mounted at \`/ROMs\` inside Desktop too, so Cemu's own File → Load
|
||||
can browse straight to \`/ROMs/wiiu/\` without needing ES-DE at all.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user