diff --git a/README.md b/README.md index a742e19..c1ee024 100644 --- a/README.md +++ b/README.md @@ -8,18 +8,55 @@ A guide for installing the Battle.net launcher on Arch Linux (2026). ## TL;DR — Which method should I use? -| Method | Best for | Effort | -|---|---|---| -| **umu-launcher (standalone Proton, no Steam)** | Scripting, minimal installs, homelab. **Recommended.** | Low | -| **Steam + Proton-GE (non-Steam shortcut)** | People who already run Steam. | Low | -| **Lutris (official installer script)** | People who want a GUI game manager. | Low–Medium | -| **Bottles (Flatpak, Gaming environment)** | Flatpak-first setups. | Low | -| **Manual `wine-staging` prefix** | Pure-Wine purists, non-Proton setups. | High | +| Method | Best for | Effort | +| ---------------------------------------------- | ------------------------------------------------------ | ---------- | +| **umu-launcher (standalone Proton, no Steam)** | Scripting, minimal installs, homelab. **Recommended.** | Low | +| **Steam + Proton-GE (non-Steam shortcut)** | People who already run Steam. | Low | +| **Lutris (official installer script)** | People who want a GUI game manager. | Low–Medium | +| **Bottles (Flatpak, Gaming environment)** | Flatpak-first setups. | Low | +| **Manual `wine-staging` prefix** | Pure-Wine purists, non-Proton setups. | High | If you just want it to work: use **Method 1 (umu)**. It gives you Proton-grade compatibility without Steam, and the upstream protonfix for Battle.net automatically applies the workarounds you'd otherwise have to remember. --- +## Quick start — automated script + +The fastest path is the included setup script, which handles everything in Method 1 automatically: + +```bash +# Download and run +curl -LO https://raw.githubusercontent.com/your-repo/battlenet-umu-setup.sh +chmod +x battlenet-umu-setup.sh +./battlenet-umu-setup.sh +``` + +Or if you already have this repo cloned: + +```bash +./battlenet-umu-setup.sh +``` + +The script is idempotent — safe to re-run. Common flags: + +```bash +./battlenet-umu-setup.sh --yes # non-interactive / CI +./battlenet-umu-setup.sh --reinstall # wipe prefix and start over +./battlenet-umu-setup.sh --proton-version=GE-Proton9-20 # pin a specific Proton version +./battlenet-umu-setup.sh --gpu=nvidia # override GPU auto-detection +./battlenet-umu-setup.sh --prefix-dir=~/Games/bnet # custom prefix location +./battlenet-umu-setup.sh --skip-packages # skip the package check +``` + +Companion scripts also included: + +| Script | Purpose | +| ----------------------------- | ------------------------------------------------------------- | +| `battlenet-update-proton.sh` | Download a new GE-Proton release and update the launch script | +| `battlenet-diagnose.sh` | Check setup health and report anything broken | + +--- + ## Prerequisites Enable `[multilib]` in `/etc/pacman.conf` if you haven't already. Uncomment these two lines: @@ -66,7 +103,17 @@ sudo pacman -S --needed \ cups samba ``` -For an NVIDIA GPU, also install `lib32-nvidia-utils`. For AMD/Intel, `lib32-mesa` and `lib32-vulkan-radeon` or `lib32-vulkan-intel`. +### GPU-specific packages + +Install the right set for your GPU — getting this wrong is the most common reason a successful install fails to render: + +| GPU | Extra packages to install | +| ------------ | ---------------------------------------------------------------- | +| **NVIDIA** | `lib32-nvidia-utils nvidia-utils` | +| **AMD** | `lib32-mesa lib32-vulkan-radeon vulkan-radeon mesa` | +| **Intel** | `lib32-mesa lib32-vulkan-intel vulkan-intel mesa` | + +The `battlenet-umu-setup.sh` script auto-detects your GPU and installs the correct set. Override with `--gpu=nvidia|amd|intel` if detection is wrong. --- @@ -85,13 +132,18 @@ For an NVIDIA GPU, also install `lib32-nvidia-utils`. For AMD/Intel, `lib32-mesa [`umu-launcher`](https://github.com/Open-Wine-Components/umu-launcher) is the Open Wine Components project (from the GE-Proton maintainers) that makes Proton usable outside Steam. It manages Proton versions, runs games inside the Steam Runtime container, and automatically applies **protonfixes** — including the upstream fix for Battle.net that handles the `WINE_SIMULATE_WRITECOPY=1` workaround for you. -### 1. Install umu-launcher +### 1. Install prerequisites and umu-launcher + +Install the prerequisite packages from [Prerequisites](#prerequisites) above, then: ```bash sudo pacman -S umu-launcher ``` +Alternatively, `battlenet-umu-setup.sh` handles both in one step. + Verify: + ```bash umu-run --help ``` @@ -114,6 +166,7 @@ umu-run ~/Downloads/Battle.net-Setup.exe ``` umu will: + - Download the latest GE-Proton into `~/.local/share/Steam/compatibilitytools.d/` if it isn't there. - Create the prefix at `~/Games/battlenet-umu`. - Look up `umu-battlenet` in the protonfixes database and pre-apply the needed env vars and tweaks. @@ -123,6 +176,7 @@ Complete the installer at the default path. ### 4. Create a launch script `~/.local/bin/battlenet`: + ```bash #!/bin/sh export WINEPREFIX="$HOME/Games/battlenet-umu" @@ -135,18 +189,20 @@ exec umu-run "$WINEPREFIX/drive_c/Program Files (x86)/Battle.net/Battle.net Laun chmod +x ~/.local/bin/battlenet ``` -Make sure `~/.local/bin` is on your `PATH` (it is by default on most Arch shells). Now `battlenet` in any terminal starts the launcher. +Make sure `~/.local/bin` is on your `PATH`. See [Adding ~/.local/bin to PATH](#adding-localbin-to-path) below if it isn't. Once it is, `battlenet` in any terminal starts the launcher. ### 5. (Optional) Desktop entry `~/.local/share/applications/battlenet.desktop`: + ```ini [Desktop Entry] Name=Battle.net -Exec=/home/YOUR_USERNAME/.local/bin/battlenet +Exec=$HOME/.local/bin/battlenet Icon=battlenet Type=Application Categories=Game; +StartupWMClass=battle.net.exe ``` ### 6. (Optional) Pin a specific Proton version @@ -157,7 +213,7 @@ If a GE-Proton release ever regresses Battle.net, pin a known-good one instead o export PROTONPATH="GE-Proton9-20" ``` -It needs to exist in `~/.local/share/Steam/compatibilitytools.d/`. +It needs to exist in `~/.local/share/Steam/compatibilitytools.d/`. With the setup script, use `--proton-version=GE-Proton9-20`. --- @@ -175,7 +231,7 @@ yay -S protonup-qt # or use paru, or grab the Flatpak Log into Steam at least once. Open ProtonUp-Qt, point it at your Steam install, and install the latest **GE-Proton**. Restart Steam. -### 2. Download the Battle.net installer +### 2. Get the Battle.net installer Get `Battle.net-Setup.exe` from . @@ -184,9 +240,11 @@ Get `Battle.net-Setup.exe` from . 1. In Steam: **Games → Add a Non-Steam Game to My Library → Browse** and select `Battle.net-Setup.exe`. 2. Right-click the new entry → **Properties → Compatibility** → tick **Force the use of a specific Steam Play compatibility tool** → select the latest **GE-Proton**. 3. Under **Launch Options**, paste: - ``` + + ```text WINE_SIMULATE_WRITECOPY=1 %command% ``` + This works around a CEF/login-screen issue that Blizzard periodically reintroduces with launcher updates. Cheap insurance. 4. Launch it from Steam and complete the installer at the default path. @@ -226,9 +284,9 @@ sudo pacman -S lutris Right-click the Battle.net entry → **Configure → System options → Environment variables**: -| Key | Value | -|---|---| -| `WINE_SIMULATE_WRITECOPY` | `1` | +| Key | Value | +| -------------------------- | ----- | +| `WINE_SIMULATE_WRITECOPY` | `1` | --- @@ -288,6 +346,7 @@ WINE_SIMULATE_WRITECOPY=1 \ ### 4. Launch scripts `~/.local/bin/battlenet`: + ```bash #!/bin/sh export WINEPREFIX="$HOME/Games/battlenet-wine-prefix" @@ -296,6 +355,7 @@ exec wine "$WINEPREFIX/drive_c/Program Files (x86)/Battle.net/Battle.net Launche ``` `~/.local/bin/battlenetkill`: + ```bash #!/bin/sh export WINEPREFIX="$HOME/Games/battlenet-wine-prefix" @@ -308,6 +368,55 @@ chmod +x ~/.local/bin/battlenet ~/.local/bin/battlenetkill --- +## Adding ~/.local/bin to PATH + +Most Arch shell setups include `~/.local/bin` automatically, but if yours doesn't: + +**bash** — add to `~/.bashrc` or `~/.bash_profile`: + +```bash +export PATH="$HOME/.local/bin:$PATH" +``` + +**zsh** — add to `~/.zshrc`: + +```zsh +export PATH="$HOME/.local/bin:$PATH" +``` + +**fish** — run once; it persists across sessions: + +```fish +fish_add_path $HOME/.local/bin +``` + +--- + +## Game-specific notes + +### World of Warcraft + +Runs well out of the box on GE-Proton. A few tuning points: + +- **D3D12 vs D3D11:** WoW defaults to D3D12 on recent clients. If you see crashes at launch, switch to D3D11 in the WoW graphics settings or add `PROTON_USE_WINED3D=0` and try D3D11 explicitly. +- **High CPU usage at login screen:** Known issue with some GE-Proton builds. Pinning a slightly older release (e.g. `GE-Proton9-15`) often resolves it until the next fix lands. + +### Overwatch 2 + +- Uses the **BattlEye** anti-cheat. BattlEye has official Linux support via the runtime bundled with Proton-GE; you do **not** need to disable it. +- Add `PROTON_ENABLE_NVAPI=1` if you have NVIDIA and see the game refuse to launch with a GPU compatibility error. + +### Diablo IV + +- Runs well on GE-Proton. Uses **Warden** (no dedicated Linux anti-cheat binary) — no special config needed. +- If you see corruption in cut-scenes, try `DXVK_ASYNC=1` in your launch script (async shader compilation). + +### Hearthstone + +Historically the most problematic title on Wine. With umu + protonfixes it works, but the login screen is particularly sensitive to `WINE_SIMULATE_WRITECOPY=1`. Make sure it's set (umu sets it automatically via protonfixes). + +--- + ## Troubleshooting ### "Battle.net Update Agent went to sleep. Attempting to wake it up… BLZBNTBNA00000005" @@ -316,11 +425,13 @@ The most common 2025–2026 failure mode. In order: 1. Confirm `WINE_SIMULATE_WRITECOPY=1` is set (or that you're using umu/Lutris-via-umu, which sets it). 2. Kill everything and wipe the agent cache: + ```bash pkill -9 Battle.net; pkill -9 Agent; pkill -9 Blizzard rm -rf "$WINEPREFIX/drive_c/ProgramData/Battle.net/Agent" rm -rf "$WINEPREFIX/drive_c/ProgramData/Blizzard Entertainment" ``` + Restart the launcher; you'll have to log in again. 3. Update GE-Proton. Fixes for Battle.net regressions usually land in a new GE-Proton release within days. @@ -330,13 +441,33 @@ The most common 2025–2026 failure mode. In order: ### Blank window / broken input under Wayland -Wine's native Wayland driver is still rough for the Battle.net launcher in 2026. Either: -- Log into an X11 session (SDDM/GDM login screen → session picker), or -- Make sure you're going through XWayland — Steam/Proton does this automatically. +Wine's native Wayland driver is still rough for the Battle.net launcher in 2026. The fix is to force XWayland explicitly: + +```bash +DISPLAY=:0 SDL_VIDEODRIVER=x11 battlenet +``` + +Or add the overrides permanently to your launch script (`~/.local/bin/battlenet`): + +```sh +export DISPLAY="${DISPLAY:-:0}" +export SDL_VIDEODRIVER=x11 +``` + +If `DISPLAY=:0` doesn't work, find the correct display number: + +```bash +ls /tmp/.X11-unix/ +``` + +Each `X` file corresponds to `DISPLAY=:`. Use whichever exists. + +As a fallback, log into a plain **X11 session** from your display manager's session picker (SDDM/GDM). Steam/Proton routes through XWayland automatically, which is why Method 2 works without this tweak. ### "X Error of failed request: BadWindow" or X11 root-access errors Caused by running Wine as root (what the old guide told you to do). **Don't.** If you're stuck with a root-owned prefix, remove it and start over as your normal user: + ```bash sudo rm -rf /home//Games/battlenet-wine-prefix ``` @@ -344,12 +475,57 @@ sudo rm -rf /home//Games/battlenet-wine-prefix ### Something else is broken Delete the prefix and start over — this really does fix most weird states. + ```bash rm -rf "$HOME/Games/battlenet-umu" # or battlenet-wine-prefix ``` --- +## systemd user service (optional) + +If you want to start and stop Battle.net with `systemctl` rather than a terminal command, create a user service unit: + +`~/.config/systemd/user/battlenet.service`: + +```ini +[Unit] +Description=Battle.net launcher (umu/Proton) +After=graphical-session.target + +[Service] +Type=simple +Environment=DISPLAY=:0 +ExecStart=%h/.local/bin/battlenet +ExecStop=%h/.local/bin/battlenetkill +Restart=no + +[Install] +WantedBy=default.target +``` + +Enable and control it: + +```bash +systemctl --user daemon-reload + +# Start +systemctl --user start battlenet + +# Stop +systemctl --user stop battlenet + +# Start automatically at login +systemctl --user enable battlenet + +# View logs +journalctl --user -u battlenet -f +``` + +> **Note:** The `DISPLAY=:0` line assumes a single-user X11 or XWayland session. If your display number differs (check `ls /tmp/.X11-unix/`), update it. Under a proper XDG session you can also use `%I` socket activation, but for a game launcher `Type=simple` with a fixed display is simpler and more reliable. + +--- + ## A note on bans Blizzard does not ban for running via Wine/Proton/Linux. Their Warden anti-cheat targets cheat software inside the game process, not the compatibility layer. This has been confirmed by Blizzard support as recently as 2025. @@ -369,4 +545,13 @@ Blizzard does not ban for running via Wine/Proton/Linux. Their Warden anti-cheat - Added `WINE_SIMULATE_WRITECOPY=1` workaround for the modern CEF login/agent issues. - Replaced `/usr/bin/` install of user launch scripts with `~/.local/bin/`. - Set default Wine Windows version to Windows 10 rather than Windows 7. -- Added Wayland/XWayland notes and the Agent-cache wipe fix for `BLZBNTBNA00000005`. \ No newline at end of file +- Added Wayland/XWayland notes and the Agent-cache wipe fix for `BLZBNTBNA00000005`. +- Added GPU-specific package table to Prerequisites. +- Added shell-specific PATH setup instructions (bash/zsh/fish). +- Fixed desktop entry to use `$HOME` instead of hardcoded username. +- Added game-specific notes for WoW, Overwatch 2, Diablo IV, and Hearthstone. +- Added Quick start section with script flags and companion script table. +- Added explicit XWayland fix (`DISPLAY=:0 SDL_VIDEODRIVER=x11`) to Wayland troubleshooting. +- Added systemd user service unit for start/stop via `systemctl`. +- Added `battlenet-update-proton.sh` — GE-Proton version manager. +- Added `battlenet-diagnose.sh` — setup health checker. diff --git a/battlenet-diagnose.sh b/battlenet-diagnose.sh new file mode 100755 index 0000000..18d34a6 --- /dev/null +++ b/battlenet-diagnose.sh @@ -0,0 +1,268 @@ +#!/usr/bin/env bash +# +# battlenet-diagnose.sh +# +# Checks the health of your Battle.net / umu-launcher setup and reports +# anything that looks wrong. Safe to run at any time — read-only, no changes. +# +# Usage: +# ./battlenet-diagnose.sh +# ./battlenet-diagnose.sh --prefix-dir=PATH # check a non-default prefix + +set -uo pipefail + +# ---------- config ---------- +PREFIX_DIR="${BATTLENET_PREFIX:-$HOME/Games/battlenet-umu}" +LAUNCH_SCRIPT="${HOME}/.local/bin/battlenet" +PROTON_COMPAT_DIR="${HOME}/.local/share/Steam/compatibilitytools.d" +LAUNCHER_EXE_REL="drive_c/Program Files (x86)/Battle.net/Battle.net Launcher.exe" + +for arg in "$@"; do + case "$arg" in + --prefix-dir=*) PREFIX_DIR="${arg#*=}" ;; + -h|--help) grep '^#' "$0" | grep -v '^#!/' | sed 's/^# \{0,1\}//'; exit 0 ;; + *) echo "Unknown argument: $arg" >&2; exit 1 ;; + esac +done + +# ---------- helpers ---------- +c_reset=$'\033[0m' +c_green=$'\033[1;32m'; c_yellow=$'\033[1;33m'; c_red=$'\033[1;31m' +c_blue=$'\033[1;34m'; c_dim=$'\033[2m'; c_bold=$'\033[1m' + +pass() { printf ' %s✓%s %s\n' "$c_green" "$c_reset" "$*"; } +fail() { printf ' %s✗%s %s\n' "$c_red" "$c_reset" "$*"; ISSUES=$(( ISSUES + 1 )); } +warn() { printf ' %s!%s %s\n' "$c_yellow" "$c_reset" "$*"; WARNINGS=$(( WARNINGS + 1 )); } +info() { printf ' %s·%s %s\n' "$c_dim" "$c_reset" "$*"; } +section() { printf '\n%s%s%s\n' "$c_bold$c_blue" "$*" "$c_reset"; } + +ISSUES=0 +WARNINGS=0 + +# ---------- system ---------- +section "System" + +info "Kernel: $(uname -r)" +info "Date: $(date)" + +if [[ $EUID -eq 0 ]]; then + fail "Running as root — Wine/Proton must run as a regular user" +else + pass "Not running as root" +fi + +if pacman -Sl multilib >/dev/null 2>&1; then + pass "[multilib] enabled" +else + fail "[multilib] not enabled — required for 32-bit libraries" +fi + +# ---------- GPU & drivers ---------- +section "GPU & Drivers" + +GPU_LINE="" +if command -v lspci >/dev/null 2>&1; then + GPU_LINE=$(lspci | grep -i "VGA\|3D\|Display" | head -1 || true) + if [[ -n "$GPU_LINE" ]]; then + info "GPU: $GPU_LINE" + else + warn "No VGA/3D/Display device found via lspci" + fi +else + warn "lspci not found (install pciutils) — GPU detection skipped" +fi + +# Vulkan +if command -v vulkaninfo >/dev/null 2>&1; then + if vulkaninfo 2>/dev/null | grep -q "apiVersion"; then + VK_VER=$(vulkaninfo 2>/dev/null | grep "apiVersion" | head -1 | sed 's/.*= *//') + pass "Vulkan available (apiVersion: $VK_VER)" + else + fail "vulkaninfo ran but reported no Vulkan devices" + fi +else + warn "vulkaninfo not found — install vulkan-tools to verify Vulkan support" +fi + +# 32-bit Vulkan ICD loader +if pacman -Qi lib32-vulkan-icd-loader >/dev/null 2>&1; then + pass "lib32-vulkan-icd-loader installed" +else + fail "lib32-vulkan-icd-loader missing — install with: sudo pacman -S lib32-vulkan-icd-loader" +fi + +# ---------- umu-launcher ---------- +section "umu-launcher" + +if command -v umu-run >/dev/null 2>&1; then + UMU_PATH=$(command -v umu-run) + UMU_VER=$(umu-run --version 2>/dev/null | head -1 || echo "unknown") + pass "umu-run found: $UMU_PATH ($UMU_VER)" +else + fail "umu-run not on PATH — install with: sudo pacman -S umu-launcher" +fi + +# ---------- GE-Proton ---------- +section "GE-Proton" + +if [[ -d "$PROTON_COMPAT_DIR" ]]; then + mapfile -t PROTON_VERSIONS < <(find "$PROTON_COMPAT_DIR" -maxdepth 1 -type d -name "GE-Proton*" | sort -rV) + if [[ ${#PROTON_VERSIONS[@]} -gt 0 ]]; then + pass "${#PROTON_VERSIONS[@]} GE-Proton version(s) installed:" + for v in "${PROTON_VERSIONS[@]}"; do + info " $(basename "$v")" + done + else + warn "No GE-Proton versions found in $PROTON_COMPAT_DIR" + warn "umu-run will download GE-Proton automatically on first launch" + fi +else + warn "Proton compat dir not found: $PROTON_COMPAT_DIR" + warn "It will be created when umu-run downloads GE-Proton" +fi + +# Check what the launch script is pinned to +if [[ -f "$LAUNCH_SCRIPT" ]]; then + PINNED=$(grep '^export PROTONPATH=' "$LAUNCH_SCRIPT" 2>/dev/null | head -1 \ + | sed 's/.*="\?\([^"]*\)"\?.*/\1/' || echo "") + if [[ -n "$PINNED" ]]; then + info "Launch script pinned to: $PINNED" + if [[ "$PINNED" == "GE-Proton" ]]; then + info "(tracking latest — will auto-download if not present)" + elif [[ -d "$PROTON_COMPAT_DIR/$PINNED" ]]; then + pass "Pinned version is installed" + else + fail "Pinned version '$PINNED' not found in $PROTON_COMPAT_DIR" + fail "Run: ./battlenet-update-proton.sh --version=$PINNED" + fi + fi +fi + +# ---------- prefix ---------- +section "Wine Prefix ($PREFIX_DIR)" + +if [[ -d "$PREFIX_DIR" ]]; then + pass "Prefix directory exists" + + LAUNCHER_PATH="$PREFIX_DIR/$LAUNCHER_EXE_REL" + if [[ -f "$LAUNCHER_PATH" ]]; then + pass "Battle.net Launcher.exe present" + else + fail "Battle.net Launcher.exe NOT found at expected path" + fail " Expected: $LAUNCHER_PATH" + fail "Re-run battlenet-umu-setup.sh (or use --reinstall)" + fi + + # Check for common stuck-agent cache + AGENT_LOCK="$PREFIX_DIR/drive_c/ProgramData/Battle.net/Agent/agent.lock" + if [[ -f "$AGENT_LOCK" ]]; then + LOCK_AGE=$(( $(date +%s) - $(stat -c%Y "$AGENT_LOCK") )) + if (( LOCK_AGE > 300 )); then + warn "Stale agent.lock found (${LOCK_AGE}s old) — may cause BLZBNTBNA00000005" + warn "Fix: battlenetkill && rm -f \"$AGENT_LOCK\"" + else + info "agent.lock exists but is fresh (${LOCK_AGE}s) — launcher may be running" + fi + fi + + # Check prefix ownership + if [[ -O "$PREFIX_DIR" ]]; then + pass "Prefix is owned by current user" + else + fail "Prefix is NOT owned by current user — Wine will misbehave" + fail "Fix: sudo chown -R $(whoami): \"$PREFIX_DIR\"" + fi +else + fail "Prefix directory does not exist: $PREFIX_DIR" + fail "Run battlenet-umu-setup.sh to create it" +fi + +# ---------- launch scripts ---------- +section "Launch Scripts" + +if [[ -f "$LAUNCH_SCRIPT" ]]; then + pass "Launch script exists: $LAUNCH_SCRIPT" + if [[ -x "$LAUNCH_SCRIPT" ]]; then + pass "Launch script is executable" + else + fail "Launch script is NOT executable — run: chmod +x $LAUNCH_SCRIPT" + fi + + # Verify env vars are present + for var in WINEPREFIX GAMEID PROTONPATH; do + if grep -q "^export $var=" "$LAUNCH_SCRIPT"; then + VAL=$(grep "^export $var=" "$LAUNCH_SCRIPT" | head -1 | sed 's/.*="\?\([^"]*\)"\?.*/\1/') + info "$var=$VAL" + else + fail "Launch script missing: export $var=..." + fi + done +else + fail "Launch script not found: $LAUNCH_SCRIPT" + fail "Run battlenet-umu-setup.sh to create it" +fi + +KILL_SCRIPT="$HOME/.local/bin/battlenetkill" +if [[ -f "$KILL_SCRIPT" && -x "$KILL_SCRIPT" ]]; then + pass "Kill script exists and is executable: $KILL_SCRIPT" +else + warn "Kill script missing or not executable: $KILL_SCRIPT" +fi + +# ---------- display / Wayland ---------- +section "Display" + +if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then + info "Wayland session detected (WAYLAND_DISPLAY=$WAYLAND_DISPLAY)" + if [[ -n "${DISPLAY:-}" ]]; then + pass "XWayland is available (DISPLAY=$DISPLAY)" + info "Battle.net will run via XWayland — this is correct" + else + fail "No DISPLAY set — XWayland does not appear to be running" + fail "Battle.net requires XWayland under Wayland" + fail "Make sure your compositor starts XWayland (most do by default)" + fi +elif [[ -n "${DISPLAY:-}" ]]; then + pass "X11 session (DISPLAY=$DISPLAY)" +else + warn "Neither DISPLAY nor WAYLAND_DISPLAY is set — running headless?" +fi + +# ---------- running processes ---------- +section "Running Processes" + +BN_PROCS=$(pgrep -fl 'Battle\.net\|battle\.net\|bnet' 2>/dev/null || true) +AGENT_PROCS=$(pgrep -fl 'Agent\.exe' 2>/dev/null || true) +WINE_PROCS=$(pgrep -fl 'wine\|wineserver\|proton' 2>/dev/null | grep -v 'grep' || true) + +if [[ -n "$BN_PROCS" ]]; then + info "Battle.net process(es) running:" + while IFS= read -r line; do info " $line"; done <<< "$BN_PROCS" +else + info "No Battle.net processes running" +fi + +if [[ -n "$AGENT_PROCS" ]]; then + info "Agent process(es) running:" + while IFS= read -r line; do info " $line"; done <<< "$AGENT_PROCS" +fi + +if [[ -n "$WINE_PROCS" ]]; then + info "Wine/Proton process(es) running:" + while IFS= read -r line; do info " $line"; done <<< "$WINE_PROCS" +fi + +# ---------- summary ---------- +echo +printf '%s%s%s\n' "$c_bold$c_blue" "=== Summary ===" "$c_reset" + +if [[ $ISSUES -eq 0 && $WARNINGS -eq 0 ]]; then + printf '%s✓ Everything looks good.%s\n' "$c_green" "$c_reset" +elif [[ $ISSUES -eq 0 ]]; then + printf '%s! %d warning(s). Battle.net should work, but check the items above.%s\n' \ + "$c_yellow" "$WARNINGS" "$c_reset" +else + printf '%s✗ %d issue(s), %d warning(s). See items marked ✗ above.%s\n' \ + "$c_red" "$ISSUES" "$WARNINGS" "$c_reset" +fi +echo diff --git a/battlenet-umu-setup.sh b/battlenet-umu-setup.sh index 871bcf7..cadd8ab 100644 --- a/battlenet-umu-setup.sh +++ b/battlenet-umu-setup.sh @@ -6,9 +6,14 @@ # Idempotent: safe to re-run. Skips steps that are already done. # # Usage: -# ./battlenet-umu-setup.sh # interactive -# ./battlenet-umu-setup.sh --yes # non-interactive, assume yes -# ./battlenet-umu-setup.sh --reinstall # wipe prefix and reinstall +# ./battlenet-umu-setup.sh # interactive +# ./battlenet-umu-setup.sh --yes # non-interactive, assume yes +# ./battlenet-umu-setup.sh --reinstall # wipe prefix and reinstall +# ./battlenet-umu-setup.sh --proton-version=GE-Proton9-20 # pin a specific Proton version +# ./battlenet-umu-setup.sh --gpu=nvidia|amd|intel # override GPU detection +# ./battlenet-umu-setup.sh --prefix-dir=PATH # override prefix location +# ./battlenet-umu-setup.sh --skip-packages # skip prerequisite package check +# END_HELP # # Does NOT run itself as root. It will call sudo only for pacman. @@ -17,27 +22,33 @@ set -euo pipefail # ---------- config ---------- PREFIX_DIR="${BATTLENET_PREFIX:-$HOME/Games/battlenet-umu}" GAMEID="umu-battlenet" -PROTONPATH="${PROTONPATH:-GE-Proton}" +PROTON_VERSION="GE-Proton" # default; overridden by --proton-version or PROTONPATH env var INSTALLER_URL="https://downloader.battle.net/download/getInstaller?os=win&installer=Battle.net-Setup.exe" INSTALLER_PATH="$HOME/Downloads/Battle.net-Setup.exe" LAUNCHER_EXE_REL="drive_c/Program Files (x86)/Battle.net/Battle.net Launcher.exe" BIN_DIR="$HOME/.local/bin" DESKTOP_DIR="$HOME/.local/share/applications" +ICON_DIR="$HOME/.local/share/icons/hicolor/256x256/apps" LAUNCH_SCRIPT="$BIN_DIR/battlenet" KILL_SCRIPT="$BIN_DIR/battlenetkill" DESKTOP_FILE="$DESKTOP_DIR/battlenet.desktop" ASSUME_YES=0 REINSTALL=0 +SKIP_PACKAGES=0 +GPU_TYPE="" # auto-detected; override with --gpu=nvidia|amd|intel + +# Honour PROTONPATH env var as a default, but let --proton-version override it +[[ -n "${PROTONPATH:-}" ]] && PROTON_VERSION="$PROTONPATH" # ---------- helpers ---------- c_reset=$'\033[0m'; c_blue=$'\033[1;34m'; c_green=$'\033[1;32m' c_yellow=$'\033[1;33m'; c_red=$'\033[1;31m'; c_dim=$'\033[2m' -log() { printf '%s==>%s %s\n' "$c_blue" "$c_reset" "$*"; } -ok() { printf '%s✓%s %s\n' "$c_green" "$c_reset" "$*"; } +log() { printf '%s==>%s %s\n' "$c_blue" "$c_reset" "$*"; } +ok() { printf '%s✓%s %s\n' "$c_green" "$c_reset" "$*"; } warn() { printf '%s!%s %s\n' "$c_yellow" "$c_reset" "$*"; } -err() { printf '%s✗%s %s\n' "$c_red" "$c_reset" "$*" >&2; } +err() { printf '%s✗%s %s\n' "$c_red" "$c_reset" "$*" >&2; } skip() { printf ' %s(skip) %s%s\n' "$c_dim" "$*" "$c_reset"; } confirm() { @@ -54,23 +65,32 @@ die() { err "$*"; exit 1; } # ---------- arg parsing ---------- for arg in "$@"; do case "$arg" in - -y|--yes) ASSUME_YES=1 ;; - --reinstall) REINSTALL=1 ;; + -y|--yes) ASSUME_YES=1 ;; + --reinstall) REINSTALL=1 ;; + --skip-packages) SKIP_PACKAGES=1 ;; + --proton-version=*) PROTON_VERSION="${arg#*=}" ;; + --prefix-dir=*) PREFIX_DIR="${arg#*=}" ;; + --gpu=nvidia|--gpu=amd|--gpu=intel) + GPU_TYPE="${arg#*=}" ;; -h|--help) - sed -n '2,12p' "$0" | sed 's/^# \{0,1\}//' + sed -n '/^# Usage:/,/^# END_HELP/p' "$0" | sed 's/^# \{0,1\}//' exit 0 ;; *) die "Unknown argument: $arg" ;; esac done +# Expose PROTONPATH for umu-run +PROTONPATH="$PROTON_VERSION" +export PROTONPATH + # ---------- preflight ---------- log "Preflight checks" [[ $EUID -ne 0 ]] || die "Do not run this script as root. It will sudo when it needs to." command -v pacman >/dev/null || die "pacman not found — this script is for Arch Linux." -command -v curl >/dev/null || die "curl not installed. sudo pacman -S curl" +command -v curl >/dev/null || die "curl not installed. Run: sudo pacman -S curl" # multilib check if ! pacman -Sl multilib >/dev/null 2>&1; then @@ -81,6 +101,81 @@ if ! pacman -Sl multilib >/dev/null 2>&1; then fi ok "[multilib] is enabled" +# ---------- detect GPU ---------- +log "Detecting GPU" + +if [[ -z "$GPU_TYPE" ]]; then + if command -v lspci >/dev/null 2>&1; then + if lspci | grep -qi "NVIDIA"; then + GPU_TYPE="nvidia" + elif lspci | grep -qi "AMD\|Radeon"; then + GPU_TYPE="amd" + else + GPU_TYPE="intel" + fi + else + warn "lspci not found (install pciutils for auto-detection). Defaulting to intel." + warn "Override with --gpu=nvidia|amd|intel if that is wrong." + GPU_TYPE="intel" + fi +fi +ok "GPU type: $GPU_TYPE (override with --gpu=nvidia|amd|intel)" + +# ---------- install prerequisites ---------- +if [[ $SKIP_PACKAGES -eq 1 ]]; then + skip "Package check skipped (--skip-packages)" +else + log "Checking 32-bit prerequisite libraries" + + COMMON_PKGS=( + giflib lib32-giflib + libpng lib32-libpng + libldap lib32-libldap + gnutls lib32-gnutls + mpg123 lib32-mpg123 + openal lib32-openal + v4l-utils lib32-v4l-utils + libpulse lib32-libpulse + alsa-plugins lib32-alsa-plugins + alsa-lib lib32-alsa-lib + libjpeg-turbo lib32-libjpeg-turbo + sqlite lib32-sqlite + libxcomposite lib32-libxcomposite + libxinerama lib32-libxinerama + ncurses lib32-ncurses + opencl-icd-loader lib32-opencl-icd-loader + libxslt lib32-libxslt + libva lib32-libva + gtk3 lib32-gtk3 + gst-plugins-base-libs lib32-gst-plugins-base-libs + vulkan-icd-loader lib32-vulkan-icd-loader + cups samba + ) + + GPU_PKGS=() + case "$GPU_TYPE" in + nvidia) GPU_PKGS=(lib32-nvidia-utils nvidia-utils) ;; + amd) GPU_PKGS=(lib32-mesa lib32-vulkan-radeon vulkan-radeon mesa) ;; + intel) GPU_PKGS=(lib32-mesa lib32-vulkan-intel vulkan-intel mesa) ;; + esac + + ALL_PKGS=("${COMMON_PKGS[@]}" "${GPU_PKGS[@]}") + + MISSING_PKGS=() + for pkg in "${ALL_PKGS[@]}"; do + pacman -Qi "$pkg" >/dev/null 2>&1 || MISSING_PKGS+=("$pkg") + done + + if [[ ${#MISSING_PKGS[@]} -eq 0 ]]; then + skip "All prerequisite packages already installed" + else + warn "Missing packages: ${MISSING_PKGS[*]}" + confirm "Install missing prerequisite packages?" || die "Aborted." + sudo pacman -S --needed --noconfirm "${MISSING_PKGS[@]}" + ok "Prerequisite packages installed" + fi +fi + # ---------- install umu-launcher ---------- log "Installing umu-launcher" @@ -98,14 +193,34 @@ command -v umu-run >/dev/null || die "umu-run not on PATH after install — some log "Fetching Battle.net installer" mkdir -p "$(dirname "$INSTALLER_PATH")" + +_download_installer() { + curl -L --fail --progress-bar -o "$INSTALLER_PATH" "$INSTALLER_URL" + # Sanity check: Battle.net-Setup.exe is always several MB; <100 KB means a redirect page or error. + local size + size=$(stat -c%s "$INSTALLER_PATH" 2>/dev/null || echo 0) + if (( size < 102400 )); then + rm -f "$INSTALLER_PATH" + die "Downloaded file is suspiciously small (${size} bytes) — download may have failed." + fi +} + if [[ -f "$INSTALLER_PATH" ]]; then - skip "Installer already at $INSTALLER_PATH" - if confirm "Re-download to get the latest?"; then - curl -L --fail -o "$INSTALLER_PATH" "$INSTALLER_URL" - ok "Installer re-downloaded" + local_size=$(stat -c%s "$INSTALLER_PATH" 2>/dev/null || echo 0) + if (( local_size < 102400 )); then + warn "Existing installer looks corrupt (${local_size} bytes). Re-downloading." + _download_installer + ok "Installer downloaded to $INSTALLER_PATH" + else + skip "Installer already at $INSTALLER_PATH ($(( local_size / 1024 / 1024 )) MB)" + # When running non-interactively (--yes), keep the existing file; only re-download on explicit prompt. + if [[ $ASSUME_YES -eq 0 ]] && confirm "Re-download to get the latest?"; then + _download_installer + ok "Installer re-downloaded" + fi fi else - curl -L --fail -o "$INSTALLER_PATH" "$INSTALLER_URL" + _download_installer ok "Installer saved to $INSTALLER_PATH" fi @@ -146,9 +261,13 @@ else warn "umu-run exited non-zero. That's often normal for the Battle.net installer." } - # The installer sometimes drops the launcher even if it exits weirdly. - # Give it a moment, then check. - sleep 2 + # The installer sometimes exits before the launcher EXE is flushed to disk. + # Poll for up to 30 s rather than assuming a fixed delay is enough. + i=0 + while [[ ! -f "$LAUNCHER_PATH" && $i -lt 30 ]]; do + sleep 1 + (( i++ )) || true + done if [[ ! -f "$LAUNCHER_PATH" ]]; then err "Battle.net Launcher.exe not found at expected path:" @@ -159,6 +278,52 @@ else ok "Battle.net installed into prefix" fi +# ---------- icon ---------- +log "Installing icon" + +ICON_SRC="$PREFIX_DIR/drive_c/Program Files (x86)/Battle.net/Battle.net Launcher.exe" +ICON_PNG="$ICON_DIR/battlenet.png" + +_install_icon() { + local tmpdir + tmpdir=$(mktemp -d) + trap 'rm -rf "$tmpdir"' RETURN + + # Extract the largest icon group from the EXE, then convert to PNG. + # wrestool and icotool come from the 'icoutils' package. + if command -v wrestool >/dev/null && command -v icotool >/dev/null; then + if wrestool -x -t 14 "$ICON_SRC" -o "$tmpdir" 2>/dev/null; then + local ico + ico=$(find "$tmpdir" -name "*.ico" | head -1) + if [[ -n "$ico" ]]; then + mkdir -p "$ICON_DIR" + icotool -x --index=1 -o "$ICON_DIR" "$ico" 2>/dev/null \ + && mv "$ICON_DIR/"*_1_*.png "$ICON_PNG" 2>/dev/null \ + && ok "Icon installed to $ICON_PNG" \ + && return 0 + fi + fi + warn "Icon extraction failed — Battle.net may not be installed yet, or the EXE layout changed." + else + warn "icoutils (wrestool/icotool) not installed. Skipping icon extraction." + warn "Install with: sudo pacman -S icoutils" + fi + return 1 +} + +if [[ -f "$ICON_PNG" ]]; then + skip "Icon already at $ICON_PNG" +elif [[ -f "$ICON_SRC" ]]; then + _install_icon || true +else + skip "Launcher EXE not found; skipping icon (will retry on next run)" +fi + +# Refresh icon cache best-effort +if command -v gtk-update-icon-cache >/dev/null; then + gtk-update-icon-cache -f -t "$HOME/.local/share/icons/hicolor" 2>/dev/null || true +fi + # ---------- launch scripts ---------- log "Installing launch scripts to $BIN_DIR" @@ -167,54 +332,88 @@ mkdir -p "$BIN_DIR" cat > "$LAUNCH_SCRIPT" < "$KILL_SCRIPT" < "$KILL_SCRIPT" <<'KILL_EOF' #!/bin/sh # Auto-generated by battlenet-umu-setup.sh -# Kills the Battle.net launcher and all Wine/Proton processes for this prefix. -export WINEPREFIX="$PREFIX_DIR" -pkill -9 -f 'Battle\\.net' 2>/dev/null || true -pkill -9 -f 'Agent\\.exe' 2>/dev/null || true -pkill -9 -f 'Blizzard' 2>/dev/null || true +# Gracefully stops Battle.net, then force-kills after a timeout. + +_graceful_kill() { + local pattern="$1" + # Try SIGTERM first + pkill -15 -f "$pattern" 2>/dev/null || return 0 + local i=0 + while pkill -0 -f "$pattern" 2>/dev/null && [ $i -lt 5 ]; do + sleep 1 + i=$(( i + 1 )) + done + # Force-kill anything still alive + pkill -9 -f "$pattern" 2>/dev/null || true +} + +_graceful_kill 'Battle\.net' +_graceful_kill 'Agent\.exe' +_graceful_kill 'Blizzard' + # wineserver may not be on PATH in umu environments; try both. if command -v wineserver >/dev/null; then wineserver -k 2>/dev/null || true fi -EOF +KILL_EOF chmod +x "$KILL_SCRIPT" ok "Wrote $KILL_SCRIPT" -# PATH check +# PATH check — show shell-appropriate instructions case ":$PATH:" in *":$BIN_DIR:"*) ok "$BIN_DIR is on PATH" ;; - *) warn "$BIN_DIR is not on your PATH. Add this to your shell rc:" - warn " export PATH=\"\$HOME/.local/bin:\$PATH\"" ;; + *) + warn "$BIN_DIR is not on your PATH." + _shell_name="$(basename "${SHELL:-bash}")" + case "$_shell_name" in + fish) + warn "Add it permanently with:" + warn " fish_add_path \$HOME/.local/bin" + ;; + zsh) + warn "Add this to ~/.zshrc:" + warn " export PATH=\"\$HOME/.local/bin:\$PATH\"" + ;; + *) + warn "Add this to ~/.bashrc (or ~/.profile for login shells):" + warn " export PATH=\"\$HOME/.local/bin:\$PATH\"" + ;; + esac + ;; esac # ---------- desktop entry ---------- log "Installing desktop entry" +# Use extracted icon if available, fall back to generic game icon +ICON_NAME="battlenet" +[[ -f "$ICON_PNG" ]] || ICON_NAME="applications-games" + mkdir -p "$DESKTOP_DIR" cat > "$DESKTOP_FILE" </dev/null; then update-desktop-database "$DESKTOP_DIR" 2>/dev/null || true fi @@ -223,10 +422,11 @@ fi echo ok "Setup complete." echo -echo " Prefix: $PREFIX_DIR" -echo " Launcher: $LAUNCH_SCRIPT" -echo " Kill: $KILL_SCRIPT" -echo " Desktop: $DESKTOP_FILE" +echo " Prefix: $PREFIX_DIR" +echo " Proton: $PROTON_VERSION" +echo " Launcher: $LAUNCH_SCRIPT" +echo " Kill: $KILL_SCRIPT" +echo " Desktop: $DESKTOP_FILE" echo echo "Run 'battlenet' to start it, or launch from your app menu." -echo "If it hangs on 'Update Agent went to sleep', run: battlenetkill && battlenet" \ No newline at end of file +echo "If it hangs on 'Update Agent went to sleep', run: battlenetkill && battlenet" diff --git a/battlenet-update-proton.sh b/battlenet-update-proton.sh new file mode 100755 index 0000000..7086069 --- /dev/null +++ b/battlenet-update-proton.sh @@ -0,0 +1,166 @@ +#!/usr/bin/env bash +# +# battlenet-update-proton.sh +# +# Lists available GE-Proton releases from GitHub, downloads the chosen version +# into ~/.local/share/Steam/compatibilitytools.d/, and updates the battlenet +# launch script to point at it. +# +# Usage: +# ./battlenet-update-proton.sh # interactive picker +# ./battlenet-update-proton.sh --latest # auto-install the latest release +# ./battlenet-update-proton.sh --list # list available releases and exit +# ./battlenet-update-proton.sh --version=GE-Proton9-20 # install a specific release +# END_HELP + +set -euo pipefail + +# ---------- config ---------- +PROTON_DIR="${PROTON_INSTALL_DIR:-$HOME/.local/share/Steam/compatibilitytools.d}" +LAUNCH_SCRIPT="${BATTLENET_LAUNCH_SCRIPT:-$HOME/.local/bin/battlenet}" +GITHUB_API="https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases" +LIST_COUNT=10 # how many recent releases to show in the picker + +ASSUME_LATEST=0 +LIST_ONLY=0 +PINNED_VERSION="" + +# ---------- helpers ---------- +c_reset=$'\033[0m'; c_blue=$'\033[1;34m'; c_green=$'\033[1;32m' +c_yellow=$'\033[1;33m'; c_red=$'\033[1;31m'; c_dim=$'\033[2m' + +log() { printf '%s==>%s %s\n' "$c_blue" "$c_reset" "$*"; } +ok() { printf '%s✓%s %s\n' "$c_green" "$c_reset" "$*"; } +warn() { printf '%s!%s %s\n' "$c_yellow" "$c_reset" "$*"; } +err() { printf '%s✗%s %s\n' "$c_red" "$c_reset" "$*" >&2; } +skip() { printf ' %s(skip) %s%s\n' "$c_dim" "$*" "$c_reset"; } +die() { err "$*"; exit 1; } + +# ---------- arg parsing ---------- +for arg in "$@"; do + case "$arg" in + --latest) ASSUME_LATEST=1 ;; + --list) LIST_ONLY=1 ;; + --version=*) PINNED_VERSION="${arg#*=}" ;; + -h|--help) + sed -n '/^# Usage:/,/^# END_HELP/p' "$0" | sed 's/^# \{0,1\}//' + exit 0 + ;; + *) die "Unknown argument: $arg" ;; + esac +done + +# ---------- preflight ---------- +command -v curl >/dev/null || die "curl not installed. Run: sudo pacman -S curl" +command -v tar >/dev/null || die "tar not installed." + +# ---------- fetch release list ---------- +log "Fetching GE-Proton release list from GitHub" + +RELEASES_JSON=$(curl -fsSL "$GITHUB_API?per_page=$LIST_COUNT") + +# Extract tag names (GE-ProtonX-Y format) +mapfile -t RELEASE_TAGS < <( + printf '%s' "$RELEASES_JSON" \ + | grep '"tag_name"' \ + | sed 's/.*"tag_name": *"\([^"]*\)".*/\1/' +) + +[[ ${#RELEASE_TAGS[@]} -gt 0 ]] || die "No releases found — GitHub API may be rate-limited. Try again shortly." + +if [[ $LIST_ONLY -eq 1 ]]; then + echo "Recent GE-Proton releases:" + for tag in "${RELEASE_TAGS[@]}"; do + installed="" + [[ -d "$PROTON_DIR/$tag" ]] && installed=" ${c_green}(installed)${c_reset}" + printf " %s%s\n" "$tag" "$installed" + done + exit 0 +fi + +# ---------- choose version ---------- +if [[ -n "$PINNED_VERSION" ]]; then + TARGET="$PINNED_VERSION" +elif [[ $ASSUME_LATEST -eq 1 ]]; then + TARGET="${RELEASE_TAGS[0]}" + log "Latest release: $TARGET" +else + echo + echo "Recent GE-Proton releases (${c_green}green = already installed${c_reset}):" + i=1 + for tag in "${RELEASE_TAGS[@]}"; do + installed="" + [[ -d "$PROTON_DIR/$tag" ]] && installed=" ${c_green}✓${c_reset}" + printf " %2d) %s%s\n" "$i" "$tag" "$installed" + (( i++ )) + done + echo + printf "Enter number (1-%d) or a version tag [default: 1 = %s]: " "${#RELEASE_TAGS[@]}" "${RELEASE_TAGS[0]}" + read -r choice + if [[ -z "$choice" ]]; then + TARGET="${RELEASE_TAGS[0]}" + elif [[ "$choice" =~ ^[0-9]+$ ]] && (( choice >= 1 && choice <= ${#RELEASE_TAGS[@]} )); then + TARGET="${RELEASE_TAGS[$(( choice - 1 ))]}" + else + TARGET="$choice" + fi +fi + +log "Target: $TARGET" + +# ---------- download ---------- +INSTALL_PATH="$PROTON_DIR/$TARGET" + +if [[ -d "$INSTALL_PATH" ]]; then + skip "$TARGET already installed at $INSTALL_PATH" +else + # Find the .tar.gz asset URL for this release + log "Resolving download URL for $TARGET" + ASSET_URL=$( + curl -fsSL "$GITHUB_API/tags/$TARGET" \ + | grep '"browser_download_url"' \ + | grep '\.tar\.gz"' \ + | head -1 \ + | sed 's/.*"browser_download_url": *"\([^"]*\)".*/\1/' + ) + [[ -n "$ASSET_URL" ]] || die "Could not find a .tar.gz asset for $TARGET. Check the version name." + + log "Downloading $TARGET" + TMPFILE=$(mktemp --suffix=.tar.gz) + trap 'rm -f "$TMPFILE"' EXIT + + curl -L --fail --progress-bar -o "$TMPFILE" "$ASSET_URL" + + log "Extracting to $PROTON_DIR" + mkdir -p "$PROTON_DIR" + tar -xzf "$TMPFILE" -C "$PROTON_DIR" + ok "$TARGET installed to $INSTALL_PATH" +fi + +# ---------- update launch script ---------- +if [[ ! -f "$LAUNCH_SCRIPT" ]]; then + warn "Launch script not found at $LAUNCH_SCRIPT — skipping update." + warn "Run battlenet-umu-setup.sh first, or set BATTLENET_LAUNCH_SCRIPT." +else + CURRENT=$(grep '^export PROTONPATH=' "$LAUNCH_SCRIPT" 2>/dev/null | head -1 | sed 's/.*="\?\([^"]*\)"\?.*/\1/' || echo "(not set)") + if [[ "$CURRENT" == "$TARGET" ]]; then + skip "Launch script already points to $TARGET" + else + log "Updating launch script: $CURRENT → $TARGET" + sed -i \ + -e "s|^# Proton version:.*|# Proton version: $TARGET|" \ + -e "s|^export PROTONPATH=.*|export PROTONPATH=\"$TARGET\"|" \ + "$LAUNCH_SCRIPT" + ok "Launch script updated" + fi +fi + +# ---------- done ---------- +echo +ok "Done. Active Proton: $TARGET" +echo +echo " Installed at: $INSTALL_PATH" +echo " Launch script: $LAUNCH_SCRIPT" +echo +echo "Restart Battle.net to use the new Proton version." +echo "If anything regresses, re-run with --version= to roll back."