wolf: relocate Wolf's state folder to the game drive (no symlinks/vdf)
The right fix, finally. Instead of fighting Steam's libraryfolders.vdf or symlinking individual app session homes, point Wolf's entire state folder at the game drive. Steam's install, downloaded games, and Proton prefixes are born there because Wolf creates every app's session home under HOST_APPS_STATE_FOLDER. - docker-compose.yml: mount $WOLF_STATE_DIR (=<game_dir>/wolf-state) at the SAME path inside and outside the wolf container, set HOST_APPS_STATE_FOLDER and WOLF_CFG_FOLDER to it. Same-path mount is required because Wolf hands host paths to the Docker daemon when spawning sibling app containers. - config.toml now lives at $WOLF_STATE_DIR/cfg/config.toml; all WOLF_CFG references updated (install + manage.sh, via WOLF_STATE_DIR in .env). - Optional migration: move existing /etc/wolf data onto the drive on upgrade. - Removed all symlink machinery: relocate_steam_home, steam-home dir, _relocate_steam_home in manage.sh, and the fix-perms relocation step. fix-perms now just re-owns the state folder + game storage. - Dropped the redundant Steam app mount; updated README/summary text. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs
This commit is contained in:
+92
-149
@@ -200,40 +200,6 @@ fi
|
|||||||
|
|
||||||
register_service wolf gaming "Cloud gaming via Moonlight (Games-on-Whales Wolf)" 47989
|
register_service wolf gaming "Cloud gaming via Moonlight (Games-on-Whales Wolf)" 47989
|
||||||
|
|
||||||
# relocate_steam_home <game_storage_dir>
|
|
||||||
# Make Steam's home physically live on the game drive. Wolf bind-mounts each
|
|
||||||
# app's session home (/etc/wolf/<id>/Steam) to /home/retro. By replacing that
|
|
||||||
# session home with a symlink to <game_storage_dir>/steam-home, everything Steam
|
|
||||||
# writes — its install, game files, and Proton prefixes — lands on the game
|
|
||||||
# drive, and Steam's Storage screen reports that drive's free space. This is the
|
|
||||||
# reliable alternative to seeding libraryfolders.vdf, which Steam overwrites on
|
|
||||||
# every launch. Safe to run repeatedly; only acts on real dirs, never symlinks.
|
|
||||||
# Returns 0 if it relocated at least one session home.
|
|
||||||
relocate_steam_home() {
|
|
||||||
local game_dir="$1"
|
|
||||||
[ -n "$game_dir" ] || return 1
|
|
||||||
local target="$game_dir/steam-home"
|
|
||||||
mkdir -p "$target"
|
|
||||||
chown -R 1000:1000 "$target" 2>/dev/null || true
|
|
||||||
local did=0 src
|
|
||||||
for src in /etc/wolf/[0-9]*/Steam; do
|
|
||||||
[ -e "$src" ] || continue
|
|
||||||
[ -L "$src" ] && continue # already relocated
|
|
||||||
[ -d "$src" ] || continue
|
|
||||||
# Preserve an existing Steam install: move contents over once.
|
|
||||||
if [ -z "$(ls -A "$target" 2>/dev/null)" ] && [ -n "$(ls -A "$src" 2>/dev/null)" ]; then
|
|
||||||
cp -a "$src/." "$target/" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
rm -rf "$src"
|
|
||||||
ln -s "$target" "$src"
|
|
||||||
chown -h 1000:1000 "$src" 2>/dev/null || true
|
|
||||||
chown -R 1000:1000 "$target" 2>/dev/null || true
|
|
||||||
echo " Steam home → $target (via $src)"
|
|
||||||
did=1
|
|
||||||
done
|
|
||||||
return $((did ? 0 : 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
install_wolf() {
|
install_wolf() {
|
||||||
require_docker || return 1
|
require_docker || return 1
|
||||||
|
|
||||||
@@ -730,23 +696,40 @@ UDEV
|
|||||||
log_success "Game storage: $GAME_STORAGE_DIR"
|
log_success "Game storage: $GAME_STORAGE_DIR"
|
||||||
|
|
||||||
# Create the storage sub-directories
|
# Create the storage sub-directories
|
||||||
mkdir -p "$GAME_STORAGE_DIR/steam/steamapps" "$GAME_STORAGE_DIR/saves" \
|
mkdir -p "$GAME_STORAGE_DIR/saves" \
|
||||||
"$GAME_STORAGE_DIR/media" "$GAME_STORAGE_DIR/lutris" \
|
"$GAME_STORAGE_DIR/media" "$GAME_STORAGE_DIR/lutris" \
|
||||||
"$GAME_STORAGE_DIR/firefox" "$GAME_STORAGE_DIR/minecraft" \
|
"$GAME_STORAGE_DIR/firefox" "$GAME_STORAGE_DIR/minecraft" \
|
||||||
"$GAME_STORAGE_DIR/kodi" "$GAME_STORAGE_DIR/emulators"
|
"$GAME_STORAGE_DIR/kodi" "$GAME_STORAGE_DIR/emulators"
|
||||||
|
|
||||||
# Steam home lives on the game drive — the bulletproof way to make games
|
# ── Wolf state folder on the game drive ───────────────────────────────────
|
||||||
# land where the user chose. Seeding libraryfolders.vdf does NOT work:
|
# This is the clean way to keep Steam (and everything else) off the OS
|
||||||
# Steam rewrites that file on every launch and forces library "0" back to
|
# drive: relocate Wolf's ENTIRE state folder onto the game drive instead of
|
||||||
# its own install dir (/home/retro/.local/share/Steam). Instead we make
|
# symlinking individual app homes. Wolf stores each app's session home,
|
||||||
# /home/retro itself physically live on the game drive by symlinking the
|
# Steam install, downloaded games, and Proton prefixes under this folder,
|
||||||
# Wolf Steam session home (/etc/wolf/<id>/Steam) to $GAME_STORAGE_DIR/
|
# and writes its own config.toml under <state>/cfg/. We mount it at the
|
||||||
# steam-home. Then Steam's one and only library IS the game drive — its
|
# SAME path inside and outside the wolf container so that the app containers
|
||||||
# install, games, AND Proton prefixes all land there, and Steam's Storage
|
# Wolf spawns through the Docker socket (which receive HOST paths) resolve
|
||||||
# screen reports the game drive's free space. See _relocate_steam_home in
|
# correctly on the host. No symlinks, no libraryfolders.vdf, no fix-perms.
|
||||||
# manage.sh, which is run on start / fix-perms / first install.
|
local WOLF_STATE_DIR="$GAME_STORAGE_DIR/wolf-state"
|
||||||
mkdir -p "$GAME_STORAGE_DIR/steam-home"
|
local WOLF_CFG="$WOLF_STATE_DIR/cfg/config.toml"
|
||||||
chown -R 1000:1000 "$GAME_STORAGE_DIR/steam-home"
|
mkdir -p "$WOLF_STATE_DIR/cfg"
|
||||||
|
chown -R 1000:1000 "$WOLF_STATE_DIR" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Migrate any pre-existing Wolf state from the default OS-drive location so
|
||||||
|
# an upgrading user keeps their Steam install / games instead of losing them.
|
||||||
|
if [ -d /etc/wolf ] && [ -n "$(ls -A /etc/wolf 2>/dev/null)" ] \
|
||||||
|
&& [ -z "$(ls -A "$WOLF_STATE_DIR" 2>/dev/null | grep -v '^cfg$')" ]; then
|
||||||
|
local _MIGRATE=""
|
||||||
|
prompt_yn "Existing Wolf data found at /etc/wolf (OS drive). Move it to $WOLF_STATE_DIR now? (y/n):" "y" _MIGRATE
|
||||||
|
if [[ "$_MIGRATE" =~ ^[Yy]$ ]]; then
|
||||||
|
log_info "Migrating /etc/wolf → $WOLF_STATE_DIR (this can take a while for large Steam installs)..."
|
||||||
|
cp -a /etc/wolf/. "$WOLF_STATE_DIR"/ \
|
||||||
|
&& rm -rf /etc/wolf/* \
|
||||||
|
&& log_success "Migration complete — old data now on the game drive" \
|
||||||
|
|| log_warning "Migration hit an error; check $WOLF_STATE_DIR before deleting /etc/wolf"
|
||||||
|
chown -R 1000:1000 "$WOLF_STATE_DIR" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Pre-create ES-DE ROM directories so the user knows where to drop files
|
# Pre-create ES-DE ROM directories so the user knows where to drop files
|
||||||
# and ES-DE shows the system in its list immediately on first launch.
|
# and ES-DE shows the system in its list immediately on first launch.
|
||||||
@@ -856,7 +839,7 @@ UDEV
|
|||||||
|
|
||||||
# ── docker-compose.yml ────────────────────────────────────────────────────
|
# ── docker-compose.yml ────────────────────────────────────────────────────
|
||||||
log_info "Generating docker-compose.yml..."
|
log_info "Generating docker-compose.yml..."
|
||||||
mkdir -p /etc/wolf/cfg
|
mkdir -p "$WOLF_STATE_DIR/cfg"
|
||||||
mkdir -p "$WOLF_DIR"
|
mkdir -p "$WOLF_DIR"
|
||||||
ensure_docker_dir_ownership "$WOLF_DIR"
|
ensure_docker_dir_ownership "$WOLF_DIR"
|
||||||
cd "$WOLF_DIR" || return 1
|
cd "$WOLF_DIR" || return 1
|
||||||
@@ -894,13 +877,17 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
- NVIDIA_DRIVER_VOLUME_NAME=nvidia-driver-vol
|
- NVIDIA_DRIVER_VOLUME_NAME=nvidia-driver-vol
|
||||||
- HOST_APPS_STATE_FOLDER=/etc/wolf
|
# State + config live on the game drive. HOST_APPS_STATE_FOLDER must be a
|
||||||
|
# HOST path because Wolf hands it to the host Docker daemon when spawning
|
||||||
|
# app containers — so it's mounted at the SAME path inside this container.
|
||||||
|
- HOST_APPS_STATE_FOLDER=${WOLF_STATE_DIR}
|
||||||
|
- WOLF_CFG_FOLDER=${WOLF_STATE_DIR}/cfg
|
||||||
- WOLF_INTERNAL_IP=${WOLF_IP}
|
- WOLF_INTERNAL_IP=${WOLF_IP}
|
||||||
- WOLF_INTERNAL_MAC=${WOLF_MAC}
|
- WOLF_INTERNAL_MAC=${WOLF_MAC}
|
||||||
- WOLF_RENDER_NODE=${WOLF_RENDER_NODE}
|
- WOLF_RENDER_NODE=${WOLF_RENDER_NODE}
|
||||||
- LD_LIBRARY_PATH=/usr/nvidia/lib:/usr/nvidia/lib32
|
- LD_LIBRARY_PATH=/usr/nvidia/lib:/usr/nvidia/lib32
|
||||||
volumes:
|
volumes:
|
||||||
- /etc/wolf/:/etc/wolf:rw
|
- ${WOLF_STATE_DIR}:${WOLF_STATE_DIR}:rw
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:rw
|
- /var/run/docker.sock:/var/run/docker.sock:rw
|
||||||
- /dev/:/dev/:rw
|
- /dev/:/dev/:rw
|
||||||
- /run/udev:/run/udev:rw
|
- /run/udev:/run/udev:rw
|
||||||
@@ -929,6 +916,9 @@ EOF
|
|||||||
cat > .env << EOF
|
cat > .env << EOF
|
||||||
# Wolf game/ROM storage root — edit this and run ./manage.sh update-storage to apply
|
# Wolf game/ROM storage root — edit this and run ./manage.sh update-storage to apply
|
||||||
GAME_STORAGE_DIR=${GAME_STORAGE_DIR}
|
GAME_STORAGE_DIR=${GAME_STORAGE_DIR}
|
||||||
|
# Wolf state folder (config.toml + every app session home) — lives on the game
|
||||||
|
# drive so Steam installs, games, and Proton prefixes stay off the OS drive.
|
||||||
|
WOLF_STATE_DIR=${WOLF_STATE_DIR}
|
||||||
EOF
|
EOF
|
||||||
chmod 600 .env
|
chmod 600 .env
|
||||||
chown "$ACTUAL_USER:$ACTUAL_USER" .env
|
chown "$ACTUAL_USER:$ACTUAL_USER" .env
|
||||||
@@ -950,42 +940,17 @@ EOF
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
|
||||||
# Make Steam's home physically live on the game drive by symlinking each Wolf
|
# Wolf's state folder (config.toml + every app session home, Steam install,
|
||||||
# Steam session home (/etc/wolf/<id>/Steam, mounted to /home/retro) at
|
# games, Proton prefixes) lives on the game drive — see WOLF_STATE_DIR in .env.
|
||||||
# $GAME_STORAGE_DIR/steam-home. Steam then installs, stores games, and keeps
|
# Nothing here needs to relocate or symlink anything; Wolf writes straight to
|
||||||
# Proton prefixes on the game drive, and its Storage screen shows that drive's
|
# the drive because docker-compose.yml mounts it as HOST_APPS_STATE_FOLDER.
|
||||||
# free space. Seeding libraryfolders.vdf does NOT work — Steam rewrites it every
|
WOLF_STATE_DIR=$(grep '^WOLF_STATE_DIR=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d= -f2-)
|
||||||
# launch. Safe to run repeatedly; skips dirs already symlinked. Needs the Steam
|
WOLF_CFG="${WOLF_STATE_DIR:-/etc/wolf}/cfg/config.toml"
|
||||||
# app to be DISCONNECTED (no running container holding the mount).
|
|
||||||
_relocate_steam_home() {
|
|
||||||
local game_dir
|
|
||||||
game_dir=$(grep '^GAME_STORAGE_DIR=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d= -f2-)
|
|
||||||
[ -n "$game_dir" ] || return 0
|
|
||||||
local target="$game_dir/steam-home"
|
|
||||||
sudo mkdir -p "$target"
|
|
||||||
local did=0 src
|
|
||||||
for src in /etc/wolf/[0-9]*/Steam; do
|
|
||||||
[ -e "$src" ] || continue
|
|
||||||
[ -L "$src" ] && continue
|
|
||||||
[ -d "$src" ] || continue
|
|
||||||
if [ -z "$(sudo ls -A "$target" 2>/dev/null)" ] && [ -n "$(sudo ls -A "$src" 2>/dev/null)" ]; then
|
|
||||||
sudo cp -a "$src/." "$target/" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
sudo rm -rf "$src"
|
|
||||||
sudo ln -s "$target" "$src"
|
|
||||||
echo " Steam home → $target"
|
|
||||||
did=1
|
|
||||||
done
|
|
||||||
sudo chown -R 1000:1000 "$target" 2>/dev/null || true
|
|
||||||
[ "$did" = 1 ] && echo "Steam now stores everything on the game drive. Reconnect Moonlight."
|
|
||||||
}
|
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
start)
|
start)
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
echo "Wolf started. Pair Moonlight to this server's IP."
|
echo "Wolf started. Pair Moonlight to this server's IP."
|
||||||
sleep 3
|
|
||||||
_relocate_steam_home
|
|
||||||
;;
|
;;
|
||||||
stop) docker compose down ;;
|
stop) docker compose down ;;
|
||||||
restart) docker compose restart ;;
|
restart) docker compose restart ;;
|
||||||
@@ -996,8 +961,7 @@ case "$1" in
|
|||||||
docker compose up -d
|
docker compose up -d
|
||||||
;;
|
;;
|
||||||
apps|add-apps|update-storage)
|
apps|add-apps|update-storage)
|
||||||
WOLF_CFG=/etc/wolf/cfg/config.toml
|
# WOLF_CFG resolved at top of script from .env
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
|
|
||||||
# Resolve game storage dir: explicit arg > .env > prompt
|
# Resolve game storage dir: explicit arg > .env > prompt
|
||||||
GAME_DIR="${2:-}"
|
GAME_DIR="${2:-}"
|
||||||
@@ -1090,7 +1054,7 @@ CATALOG = {
|
|||||||
name='WolfSteam', title='Steam',
|
name='WolfSteam', title='Steam',
|
||||||
icon='https://games-on-whales.github.io/wildlife/apps/steam/assets/icon.png',
|
icon='https://games-on-whales.github.io/wildlife/apps/steam/assets/icon.png',
|
||||||
image='ghcr.io/games-on-whales/steam:edge',
|
image='ghcr.io/games-on-whales/steam:edge',
|
||||||
mounts=[], # Steam home is symlinked onto the game drive (relocate_steam_home)
|
mounts=[], # Steam home lives on the game drive via Wolf's state folder
|
||||||
env=['PROTON_LOG=1', 'RUN_SWAY=true',
|
env=['PROTON_LOG=1', 'RUN_SWAY=true',
|
||||||
'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*'],
|
'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*'],
|
||||||
cap_add=['SYS_ADMIN', 'SYS_NICE', 'SYS_PTRACE', 'NET_RAW', 'MKNOD', 'NET_ADMIN'],
|
cap_add=['SYS_ADMIN', 'SYS_NICE', 'SYS_PTRACE', 'NET_RAW', 'MKNOD', 'NET_ADMIN'],
|
||||||
@@ -1252,33 +1216,25 @@ PYEOF
|
|||||||
echo "Set up backups with the modular system: sudo ./setup.sh backup"
|
echo "Set up backups with the modular system: sudo ./setup.sh backup"
|
||||||
;;
|
;;
|
||||||
fix-perms)
|
fix-perms)
|
||||||
# Wolf creates each app's home directory under /etc/wolf/<session-id>/<App>
|
# Wolf creates each app's home dir under WOLF_STATE_DIR/<session-id>/<App>
|
||||||
# and mounts it as /home/retro inside the app container. If anything in
|
# and mounts it as /home/retro inside the app container. If anything in
|
||||||
# there ends up root-owned (e.g. left over from an earlier mount layout),
|
# there ends up root-owned, the in-container 'retro' user (uid 1000)
|
||||||
# the in-container 'retro' user (uid 1000) can't write to it and the app
|
# can't write to it and the app bails on startup with "Permission
|
||||||
# bails on startup with "Permission denied" creating ~/.steam, etc.
|
# denied". Re-own the whole state folder + game storage to uid/gid 1000.
|
||||||
# Re-own every Wolf app home dir to uid/gid 1000 to clear that.
|
echo "Fixing ownership of Wolf state + game storage (uid 1000)..."
|
||||||
echo "Fixing ownership of Wolf app home directories (uid 1000)..."
|
|
||||||
found=0
|
found=0
|
||||||
for d in /etc/wolf/[0-9]*/; do
|
if [ -n "$WOLF_STATE_DIR" ] && [ -d "$WOLF_STATE_DIR" ]; then
|
||||||
[ -d "$d" ] || continue
|
sudo chown -R 1000:1000 "$WOLF_STATE_DIR"
|
||||||
sudo chown -R 1000:1000 "$d"
|
echo " fixed: $WOLF_STATE_DIR"
|
||||||
echo " fixed: $d"
|
|
||||||
found=1
|
found=1
|
||||||
done
|
fi
|
||||||
# Also re-own the on-disk game storage so the retro user can write saves/installs
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
||||||
GAME_DIR=$(grep '^GAME_STORAGE_DIR=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d= -f2-)
|
GAME_DIR=$(grep '^GAME_STORAGE_DIR=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d= -f2-)
|
||||||
if [ -n "$GAME_DIR" ] && [ -d "$GAME_DIR" ]; then
|
if [ -n "$GAME_DIR" ] && [ -d "$GAME_DIR" ]; then
|
||||||
sudo chown -R 1000:1000 "$GAME_DIR"
|
sudo chown -R 1000:1000 "$GAME_DIR"
|
||||||
echo " fixed: $GAME_DIR"
|
echo " fixed: $GAME_DIR"
|
||||||
found=1
|
found=1
|
||||||
fi
|
fi
|
||||||
# Relocate Steam's home onto the game drive so games install there
|
[ "$found" = 0 ] && echo " Nothing to fix (no Wolf state dir found yet)."
|
||||||
# (and stay there). This moves an existing Steam install over once,
|
|
||||||
# then symlinks the session home — no re-download needed.
|
|
||||||
_relocate_steam_home
|
|
||||||
[ "$found" = 0 ] && echo " Nothing to fix (no Wolf app dirs found yet)."
|
|
||||||
echo "Done. Reconnect from Moonlight to relaunch the app."
|
echo "Done. Reconnect from Moonlight to relaunch the app."
|
||||||
;;
|
;;
|
||||||
pin)
|
pin)
|
||||||
@@ -1332,10 +1288,10 @@ MEOF
|
|||||||
log_info "Starting Wolf (first start pulls the image — give it a minute)..."
|
log_info "Starting Wolf (first start pulls the image — give it a minute)..."
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
|
|
||||||
# Wolf writes /etc/wolf/cfg/config.toml on first start. Wait for it, then
|
# Wolf writes $WOLF_CFG (on the game drive) on first start. Wait for it,
|
||||||
# wire in game storage.
|
# then wire in game storage.
|
||||||
log_info "Waiting for Wolf to generate /etc/wolf/cfg/config.toml..."
|
log_info "Waiting for Wolf to generate config.toml at $WOLF_CFG..."
|
||||||
local WOLF_CFG=/etc/wolf/cfg/config.toml _i
|
local _i
|
||||||
for _i in $(seq 1 30); do
|
for _i in $(seq 1 30); do
|
||||||
[ -f "$WOLF_CFG" ] && break
|
[ -f "$WOLF_CFG" ] && break
|
||||||
sleep 2
|
sleep 2
|
||||||
@@ -1362,7 +1318,7 @@ CATALOG = {
|
|||||||
name='WolfSteam', title='Steam',
|
name='WolfSteam', title='Steam',
|
||||||
icon='https://games-on-whales.github.io/wildlife/apps/steam/assets/icon.png',
|
icon='https://games-on-whales.github.io/wildlife/apps/steam/assets/icon.png',
|
||||||
image='ghcr.io/games-on-whales/steam:edge',
|
image='ghcr.io/games-on-whales/steam:edge',
|
||||||
mounts=[], # Steam home is symlinked onto the game drive (relocate_steam_home)
|
mounts=[], # Steam home lives on the game drive via Wolf's state folder
|
||||||
env=['PROTON_LOG=1', 'RUN_SWAY=true',
|
env=['PROTON_LOG=1', 'RUN_SWAY=true',
|
||||||
'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*'],
|
'GOW_REQUIRED_DEVICES=/dev/input/* /dev/dri/* /dev/nvidia*'],
|
||||||
cap_add=['SYS_ADMIN', 'SYS_NICE', 'SYS_PTRACE', 'NET_RAW', 'MKNOD', 'NET_ADMIN'],
|
cap_add=['SYS_ADMIN', 'SYS_NICE', 'SYS_PTRACE', 'NET_RAW', 'MKNOD', 'NET_ADMIN'],
|
||||||
@@ -1524,19 +1480,14 @@ if not added and not updated:
|
|||||||
PYEOF
|
PYEOF
|
||||||
docker compose restart wolf
|
docker compose restart wolf
|
||||||
log_success "Wolf restarted with updated config"
|
log_success "Wolf restarted with updated config"
|
||||||
|
# Nothing else to do for Steam storage: Wolf's state folder lives on the
|
||||||
# Point Steam's home at the game drive (see steam-home note above).
|
# game drive, so Steam installs, games, and Proton prefixes land there
|
||||||
# Wolf only creates the Steam session dir on first Moonlight connect,
|
# automatically the first time you connect. No fix-perms, no symlinks.
|
||||||
# so it almost certainly doesn't exist yet here — relocate any that do,
|
|
||||||
# and tell the user the one-time step for the rest.
|
|
||||||
if echo "$APP_KEYS" | grep -qw steam; then
|
if echo "$APP_KEYS" | grep -qw steam; then
|
||||||
relocate_steam_home "$GAME_STORAGE_DIR" || true
|
log_success "Steam will install games to $WOLF_STATE_DIR (on the game drive)"
|
||||||
log_info "Steam storage: after your FIRST Moonlight connection, run"
|
|
||||||
log_info " cd $WOLF_DIR && ./manage.sh fix-perms"
|
|
||||||
log_info "to move Steam onto the game drive, then reconnect. (Once only.)"
|
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
log_warning "Wolf config not generated in time. Add apps manually to /etc/wolf/cfg/config.toml"
|
log_warning "Wolf config not generated in time. Add apps manually to $WOLF_CFG"
|
||||||
log_warning "Then run: ./manage.sh apps"
|
log_warning "Then run: ./manage.sh apps"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1602,13 +1553,13 @@ PYEOF
|
|||||||
echo ""
|
echo ""
|
||||||
echo " ${GAME_STORAGE_DIR}/"
|
echo " ${GAME_STORAGE_DIR}/"
|
||||||
echo " roms/ → /ROMs (EmulationStation)"
|
echo " roms/ → /ROMs (EmulationStation)"
|
||||||
echo " steam/ → /home/retro/.steam (Steam Big Picture)"
|
|
||||||
echo " saves/ → /home/retro/.config/retroarch/saves (RetroArch saves)"
|
echo " saves/ → /home/retro/.config/retroarch/saves (RetroArch saves)"
|
||||||
echo " media/ → /media (ES-DE scraped artwork)"
|
echo " media/ → /media (ES-DE scraped artwork)"
|
||||||
|
echo " wolf-state/ → Wolf state: Steam install, games, Proton prefixes,"
|
||||||
|
echo " ES-DE settings, controller maps, config.toml"
|
||||||
echo ""
|
echo ""
|
||||||
echo " Other app data (ES-DE settings, controller mappings, save states,"
|
echo " Wolf's entire state folder lives on the game drive, so Steam games"
|
||||||
echo " standalone-emulator saves) is persisted by Wolf under /etc/wolf and"
|
echo " and everything else stay off the OS drive automatically."
|
||||||
echo " is included when you set up backups."
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "── APPS ──────────────────────────────────────────────"
|
echo "── APPS ──────────────────────────────────────────────"
|
||||||
echo ""
|
echo ""
|
||||||
@@ -1632,9 +1583,9 @@ PYEOF
|
|||||||
echo ""
|
echo ""
|
||||||
echo "── BACKUPS ───────────────────────────────────────────"
|
echo "── BACKUPS ───────────────────────────────────────────"
|
||||||
echo ""
|
echo ""
|
||||||
echo " Back up your saves, progress and user data (Steam user data and all of"
|
echo " Back up your saves, progress and user data (Wolf's state folder:"
|
||||||
echo " /etc/wolf: ES-DE settings, controller mappings, RetroArch saves/states,"
|
echo " ES-DE settings, controller mappings, RetroArch saves/states, emulator"
|
||||||
echo " emulator saves). ROMs and game installs are skipped."
|
echo " saves, Steam user data). ROMs and game installs are skipped."
|
||||||
echo ""
|
echo ""
|
||||||
echo " Set up automatic backups with the backup module:"
|
echo " Set up automatic backups with the backup module:"
|
||||||
echo " sudo ./setup.sh backup"
|
echo " sudo ./setup.sh backup"
|
||||||
@@ -1684,35 +1635,27 @@ cd $WOLF_DIR
|
|||||||
| 48010 | TCP | RTSP |
|
| 48010 | TCP | RTSP |
|
||||||
| 47998–48000 | UDP | RTP video/audio/control |
|
| 47998–48000 | UDP | RTP video/audio/control |
|
||||||
|
|
||||||
## Game storage: \`$GAME_STORAGE_DIR\`
|
## Storage layout
|
||||||
On-disk dirs are bind-mounted into each app container under \`/mnt/games/\`:
|
Two things live on the game drive (\`$GAME_STORAGE_DIR\`):
|
||||||
|
|
||||||
|
**1. Shared media folders** — bind-mounted into app containers:
|
||||||
- \`roms/<system>/\` → /ROMs (EmulationStation — drop ROMs here)
|
- \`roms/<system>/\` → /ROMs (EmulationStation — drop ROMs here)
|
||||||
- \`steam-home/\` → Steam's home (/home/retro) — installs, games, prefixes
|
|
||||||
- \`saves/\` → /mnt/games/saves (RetroArch saves & states)
|
- \`saves/\` → /mnt/games/saves (RetroArch saves & states)
|
||||||
- \`emulators/\` → /mnt/games/emulators (Azahar 3DS AppImage, etc.)
|
- \`emulators/\` → /mnt/games/emulators (Azahar 3DS AppImage, etc.)
|
||||||
|
|
||||||
Mounting under \`/mnt/games\` (not the container's \`/home/retro\`) avoids
|
**2. \`wolf-state/\`** — Wolf's entire state folder (\`WOLF_STATE_DIR\` in
|
||||||
clobbering the app's home directory, which Wolf manages per-session under
|
\`.env\`). This is the key to keeping games off the OS drive: it holds
|
||||||
\`/etc/wolf/<id>/<App>\`.
|
\`cfg/config.toml\` plus every app's session home, including the Steam install,
|
||||||
|
downloaded games, and Proton prefixes.
|
||||||
|
|
||||||
## Steam storage — how games land on the game drive
|
## Why games land on the game drive (no config needed)
|
||||||
Steam rewrites \`libraryfolders.vdf\` on every launch and forces its primary
|
Wolf stores all app state under \`HOST_APPS_STATE_FOLDER\`. We point that — and
|
||||||
library back to its own install dir, so seeding that file does NOT work.
|
the matching docker-compose volume — at \`wolf-state/\` on the game drive, mounted
|
||||||
Instead, this setup symlinks Steam's session home
|
at the **same path inside and outside** the wolf container so the app containers
|
||||||
(\`/etc/wolf/<id>/Steam\` → \`/home/retro\`) at \`steam-home/\` on the game
|
Wolf spawns through the Docker socket resolve it correctly on the host. Because
|
||||||
drive. Steam's one and only library is therefore the game drive — installs,
|
Steam's home is born on the game drive, its installs, games, and Proton prefixes
|
||||||
game files, and Proton prefixes all land there with nothing to configure.
|
go there with nothing to configure — no \`libraryfolders.vdf\` seeding, no
|
||||||
|
symlinks, no \`fix-perms\` dance.
|
||||||
Wolf creates the Steam session dir only on your FIRST Moonlight connection.
|
|
||||||
So the one-time sequence is:
|
|
||||||
1. Connect once and let Steam open (it lands on the OS drive this first time).
|
|
||||||
2. Disconnect, then run \`./manage.sh fix-perms\` (moves Steam to the game
|
|
||||||
drive and symlinks it — no re-download).
|
|
||||||
3. Reconnect. Steam → Settings → Storage now shows the game drive's free
|
|
||||||
space, and everything installs there from here on.
|
|
||||||
|
|
||||||
\`./manage.sh start\` re-applies this automatically, so it self-heals on
|
|
||||||
reboots.
|
|
||||||
|
|
||||||
## 3DS emulation
|
## 3DS emulation
|
||||||
Azahar (open-source Citra fork) AppImage lives in \`emulators/\` → mounted at
|
Azahar (open-source Citra fork) AppImage lives in \`emulators/\` → mounted at
|
||||||
@@ -1732,7 +1675,7 @@ Then reconnect from Moonlight.
|
|||||||
|
|
||||||
## Backup
|
## Backup
|
||||||
\`\`\`bash
|
\`\`\`bash
|
||||||
sudo ./setup.sh backup # covers /etc/wolf saves and ES-DE settings
|
sudo ./setup.sh backup # covers wolf-state/ saves and ES-DE settings
|
||||||
\`\`\`
|
\`\`\`
|
||||||
MD
|
MD
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user