Merge pull request #135 from outis1one/claude/zealous-heisenberg-8ylloi
Claude/zealous heisenberg 8ylloi
This commit is contained in:
@@ -137,3 +137,83 @@ then either:
|
||||
Tested on **Ubuntu 24.04 LTS** and **26.04 LTS**.
|
||||
Works on any Ubuntu LTS ≥ 22.04; non-LTS releases also work.
|
||||
The wizard shows the detected OS in the header and warns on unknown versions.
|
||||
|
||||
## Gaming scripts
|
||||
|
||||
Standalone scripts in `scripts/` for gaming setup — not part of the main
|
||||
wizard, run separately.
|
||||
|
||||
### Star Wars Battlefront II (2017) + Kyber
|
||||
|
||||
**`scripts/setup-swbf2-linux.sh`** — Configure SWBF2 on native Linux Steam
|
||||
(Proton, controller, performance tweaks).
|
||||
|
||||
**`scripts/setup-kyber-linux.sh`** — Install the native Linux Kyber launcher.
|
||||
|
||||
Kyber is the community multiplayer replacement for SWBF2 after EA shut down
|
||||
official servers in 2022. It went open-source (GPL) in January 2026.
|
||||
|
||||
**The correct approach is a native Linux AppImage** — not Wine or Proton for
|
||||
the launcher itself. The AppImage is maintained at:
|
||||
https://github.com/simonlinuxcraft/kyber-linuxport-unofficial
|
||||
|
||||
```bash
|
||||
chmod +x scripts/setup-kyber-linux.sh
|
||||
./scripts/setup-kyber-linux.sh
|
||||
```
|
||||
|
||||
The script downloads the latest AppImage, installs a desktop entry, and
|
||||
creates a `kyber` command in `~/.local/bin`.
|
||||
|
||||
**Every time you want to play:**
|
||||
1. Open **Steam** (must be running for library validation) — do NOT click Play on SWBF2
|
||||
2. Launch **Kyber** (`kyber` or from the app menu)
|
||||
3. In Kyber: join a server (HOME) or create one (HOST)
|
||||
4. Kyber/Maxima launches SWBF2 via its own bundled GE-Proton — wait 1-3 minutes
|
||||
5. If the SWBF2 window appears but won't focus: press **Alt+Tab** or click its
|
||||
taskbar entry — this is normal when the game is launched by a wrapper process
|
||||
|
||||
Do NOT launch SWBF2 from Steam directly. If Steam's SWBF2 is already running
|
||||
when Kyber starts, kill it first — Kyber cannot inject into a Steam-launched instance.
|
||||
|
||||
**If Kyber says "Game Not Found":**
|
||||
Click **SET GAME FOLDER** and point it to the SWBF2 install directory.
|
||||
Find it with:
|
||||
```bash
|
||||
find ~/.steam/steam/steamapps -name "starwarsbattlefrontii.exe" 2>/dev/null | head -1 | xargs dirname
|
||||
```
|
||||
Paste that path into the SET GAME FOLDER dialog.
|
||||
|
||||
**First run (one-time setup):**
|
||||
1. Click **EA Account** → log in with your EA credentials in the browser
|
||||
2. Click **Skip** on Nexus Mods (optional, only needed for mods)
|
||||
3. EA login is cached — you stay logged in across sessions
|
||||
|
||||
**Hosting a private server with bots:**
|
||||
- HOST → pick maps/modes → set a **name** and **PASSWORD** → Start Server
|
||||
- Share the server name + password with friends; they search by name in HOME
|
||||
- Bot count: in the HOST panel right side → **AUTOPLAYERS** section →
|
||||
set **BOTS TEAM 1** and **BOTS TEAM 2** (e.g. 4 each) → click **UPDATE SERVER**
|
||||
- Bot difficulty: the **BOT DIFFICULTY** slider (RECRUIT → OFFICER → KNIGHT → MASTER)
|
||||
- After the game loads you can also update settings live and hit UPDATE SERVER again
|
||||
|
||||
**Requirements:**
|
||||
- SWBF2 (Steam AppID 1237950) installed via Steam
|
||||
(Kyber manages its own GE-Proton for launching the game)
|
||||
- glibc 2.38+ — Ubuntu 24.04+, Fedora 38+, SteamOS 3.7+
|
||||
- EA account (free) at ea.com
|
||||
- Unprivileged user namespaces enabled (Ubuntu 24.04 restricts these by default):
|
||||
```bash
|
||||
sudo sysctl -w kernel.unprivileged_userns_clone=1
|
||||
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
```
|
||||
The setup script applies this automatically when run with sudo and saves it
|
||||
to `/etc/sysctl.d/99-userns.conf` to persist across reboots.
|
||||
Without this fix Kyber fails with: `bwrap: setting up uid map: Permission denied`
|
||||
|
||||
**What does NOT work:**
|
||||
- Running the Windows `kyber_launcher.exe` under Wine/Proton: EA's auth
|
||||
callback uses the `eadesktop://` URI scheme which has no Linux handler,
|
||||
and Wine's cmd.exe crashes on long OAuth URLs anyway
|
||||
- Running Kyber inside Wolf/Games-on-Whales: the Docker double-sandbox
|
||||
blocks the user namespace clone that Proton requires
|
||||
|
||||
+243
-542
@@ -1,577 +1,278 @@
|
||||
#!/bin/bash
|
||||
# setup-kyber-linux.sh — Install the Kyber Launcher for SWBF2 (2017) on a
|
||||
# native Linux Steam machine (headless or desktop) with Proton Experimental.
|
||||
# setup-kyber-linux.sh — Install the Kyber Launcher (native Linux port) for
|
||||
# SWBF2 (2017) on a native Linux Steam machine.
|
||||
#
|
||||
# Kyber is a community multiplayer client for Star Wars Battlefront II (2017)
|
||||
# after EA shut down the official servers. It is a Windows app (Flutter/Rust).
|
||||
# after EA shut down the official servers in 2022. Kyber went open-source
|
||||
# under GPL in January 2026.
|
||||
#
|
||||
# ── How Kyber's EA login actually works ────────────────────────────────────
|
||||
# ── The RIGHT way: native Linux AppImage ───────────────────────────────────
|
||||
#
|
||||
# Kyber uses the "Maxima" OAuth PKCE flow:
|
||||
# 1. Kyber starts a temporary HTTP server on 127.0.0.1 (dynamic port ~41413+)
|
||||
# 2. It calls cmd /c start "" "<EA auth URL>" to open a browser
|
||||
# 3. You log in on the EA page; EA redirects to 127.0.0.1:PORT/?code=...
|
||||
# 4. Kyber's HTTP server catches the code and exchanges it for tokens
|
||||
# As of 2026 there is an unofficial native Linux port of Kyber:
|
||||
# https://github.com/simonlinuxcraft/kyber-linuxport-unofficial
|
||||
#
|
||||
# Problem: Wine's cmd.exe crashes with STATUS_ACCESS_VIOLATION (0xC0000005)
|
||||
# when called as cmd /c start "" "<URL>". This blocks the login entirely.
|
||||
# It ships as a self-contained AppImage (x86_64). No Wine, no Proton, no
|
||||
# cmd.exe shims, no OAuth watcher daemons. EA login is handled natively by
|
||||
# the bundled Maxima service (open-source EA Desktop replacement by the
|
||||
# Armchair Developers team).
|
||||
#
|
||||
# Fix: Windows Image File Execution Options (IFEO) lets us intercept any
|
||||
# cmd.exe launch at the registry level without touching system32/cmd.exe
|
||||
# (which Proton resets on every launch). We register a tiny shim that
|
||||
# captures the http URL, writes it to a known file, and exits 0. A Linux
|
||||
# watcher picks up the file and calls xdg-open to open the URL in the
|
||||
# system browser, completing the OAuth flow normally.
|
||||
# Tested on: Ubuntu 24.04+, Fedora, SteamOS 3.7+ (requires glibc 2.38+)
|
||||
# Recommended Proton for SWBF2 itself: GE-Proton 10.x or proton-cachyos 11.x
|
||||
# (Kyber/Maxima downloads and manages its own GE-Proton automatically)
|
||||
#
|
||||
# ── Why NOT to run Kyber inside Wolf / Games-on-Whales ─────────────────────
|
||||
# ── Login flow ─────────────────────────────────────────────────────────────
|
||||
#
|
||||
# Wolf runs Docker + Proton's bwrap nested. The double-sandbox blocks
|
||||
# CLONE_NEWUSER which breaks WebView2, AND the inner bwrap prevents
|
||||
# xdg-open from reaching the host display. Kyber's loopback redirect to
|
||||
# 127.0.0.1:PORT also fails inside the container network stack. Use native
|
||||
# Linux Steam instead — it's the supported path.
|
||||
# 1. Launch the AppImage.
|
||||
# 2. Click "EA Account" — a browser window opens to accounts.ea.com.
|
||||
# 3. Log in with your EA account.
|
||||
# 4. Kyber completes authentication via Maxima (no redirect hacks needed).
|
||||
# 5. Click "Skip" on Nexus Mods if you don't use mods.
|
||||
#
|
||||
# ── WebView2 ───────────────────────────────────────────────────────────────
|
||||
# ── How to play ────────────────────────────────────────────────────────────
|
||||
#
|
||||
# WebView2 is NOT required for the login flow (Maxima handles that). This
|
||||
# script still installs the WebView2 Evergreen runtime because Kyber may use
|
||||
# it for in-app content rendering. Installing it causes no harm and prevents
|
||||
# any fallback-related error dialogs inside Kyber.
|
||||
# Kyber launches SWBF2 itself — do NOT launch SWBF2 from Steam first.
|
||||
# If Steam's SWBF2 is running when Kyber starts, kill it.
|
||||
#
|
||||
# ── Headless note ──────────────────────────────────────────────────────────
|
||||
# 1. Open Steam (must be running for library access, but do NOT click Play).
|
||||
# 2. Launch Kyber (AppImage or 'kyber' command).
|
||||
# 3. In Kyber: join a server (HOME) or create one (HOST).
|
||||
# 4. Kyber/Maxima launches SWBF2 via its own bundled GE-Proton.
|
||||
# 5. Wait for the Frostbite/SWBF2 loading screen. This takes 1-3 minutes.
|
||||
# 6. If the SWBF2 window appears but looks stuck: click its taskbar entry
|
||||
# or press Alt+Tab to bring it into focus — this is normal behaviour
|
||||
# when the game is launched by a wrapper process rather than directly.
|
||||
#
|
||||
# On a headless GPU box use Steam Remote Play: install Steam, configure a
|
||||
# virtual/dummy display so Steam has something to render to, add Kyber +
|
||||
# SWBF2, then connect with the Steam Link app from any device.
|
||||
# This script warns if no display is detected at setup time.
|
||||
# ── Hosting a private server ───────────────────────────────────────────────
|
||||
#
|
||||
# 1. In Kyber, click HOST.
|
||||
# 2. Select maps/modes for your rotation (drag into Active Rotation).
|
||||
# 3. In the right panel: set a name, set a PASSWORD (keeps it private).
|
||||
# 4. Click Settings to adjust max players etc.
|
||||
# 5. Click START SERVER.
|
||||
# 6. Share the server name + password with friends — they search by name
|
||||
# in the HOME tab and enter the password to join.
|
||||
#
|
||||
# Bots: SWBF2 fills empty player slots with AI automatically. No separate
|
||||
# bot count setting is needed — just start the server and join it.
|
||||
#
|
||||
# ── Why NOT Wine/Proton for the Kyber launcher ─────────────────────────────
|
||||
#
|
||||
# The Windows Kyber launcher (kyber_launcher.exe) has a fatal flaw on Linux:
|
||||
# its EA OAuth flow calls cmd /c start "" "<URL>" to open a browser.
|
||||
# Wine's cmd.exe crashes with STATUS_ACCESS_VIOLATION (0xC0000005) on long
|
||||
# URLs. Fixing this requires replacing Proton's own cmd.exe binary with a
|
||||
# shim — and the shim gets overwritten on every Proton update. Additionally,
|
||||
# EA's auth callback uses the eadesktop:// URI scheme (not http://127.0.0.1
|
||||
# as originally believed), which has no Linux handler. The native AppImage
|
||||
# bypasses all of this entirely.
|
||||
#
|
||||
# ── Prerequisites ──────────────────────────────────────────────────────────
|
||||
# a. Steam installed and launched at least once (native, not Flatpak ideally
|
||||
# — Flatpak works but paths differ; this script handles both).
|
||||
# b. SWBF2 (AppID 1237950) installed and working with Proton Experimental
|
||||
# (run setup-swbf2-linux.sh first).
|
||||
# c. KyberLauncher.exe downloaded from https://kyber.gg saved to
|
||||
# ~/Downloads/KyberLauncher.exe (or pass the path as $1).
|
||||
# a. SWBF2 (AppID 1237950) installed via Steam.
|
||||
# Run setup-swbf2-linux.sh first if needed.
|
||||
# NOTE: Kyber manages its own GE-Proton for launching the game — you
|
||||
# do not need to configure a Proton version for SWBF2 in Steam.
|
||||
# b. Internet access to download the AppImage (~173 MB).
|
||||
# c. glibc 2.38+ (Ubuntu 24.04+, Fedora 38+, SteamOS 3.7+).
|
||||
# On Ubuntu 22.04 the AppImage may not run — upgrade to 24.04.
|
||||
# d. Unprivileged user namespaces enabled (required for Kyber's sandbox).
|
||||
# This script checks and optionally fixes this for you (see below).
|
||||
#
|
||||
# ── Unprivileged user namespaces (bwrap) ───────────────────────────────────
|
||||
#
|
||||
# Kyber's AppImage uses bubblewrap (bwrap) for sandboxing. Ubuntu 24.04
|
||||
# restricts unprivileged user namespaces by default, which causes the error:
|
||||
# bwrap: setting up uid map: Permission denied
|
||||
#
|
||||
# Fix (this script can apply these automatically):
|
||||
# sudo sysctl -w kernel.unprivileged_userns_clone=1
|
||||
# sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
#
|
||||
# To make permanent across reboots, write to /etc/sysctl.d/99-userns.conf:
|
||||
# kernel.unprivileged_userns_clone = 1
|
||||
# kernel.apparmor_restrict_unprivileged_userns = 0
|
||||
#
|
||||
# ── "Game Not Found" dialog ────────────────────────────────────────────────
|
||||
#
|
||||
# If Kyber shows "Game Not Found" after launching:
|
||||
# 1. Click SET GAME FOLDER in Kyber.
|
||||
# 2. Run this command to find your SWBF2 install path:
|
||||
# find ~/.steam/steam/steamapps -name 'starwarsbattlefrontii.exe' 2>/dev/null | head -1 | xargs dirname
|
||||
# 3. Paste that path into the dialog. Kyber remembers it after this.
|
||||
#
|
||||
# ── Usage ──────────────────────────────────────────────────────────────────
|
||||
# chmod +x setup-kyber-linux.sh
|
||||
# ./setup-kyber-linux.sh [/path/to/KyberLauncher.exe]
|
||||
# ./setup-kyber-linux.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
KYBER_INSTALLER="${1:-$HOME/Downloads/KyberLauncher.exe}"
|
||||
KYBER_APPID="9900000001" # non-Steam shortcut appid
|
||||
KYBER_COMPAT_ID="$KYBER_APPID" # prefix dir must match appid for Steam to find it
|
||||
KYBER_REPO="simonlinuxcraft/kyber-linuxport-unofficial"
|
||||
INSTALL_DIR="$HOME/.local/share/kyber"
|
||||
DESKTOP_FILE="$HOME/.local/share/applications/kyber-launcher.desktop"
|
||||
BIN_LINK="$HOME/.local/bin/kyber"
|
||||
|
||||
echo "=== Kyber Launcher — Native Linux Steam Setup ==="
|
||||
echo "=== Kyber Launcher — Native Linux Setup ==="
|
||||
echo ""
|
||||
|
||||
# ── Locate Steam home ──────────────────────────────────────────────────────
|
||||
find_steam_home() {
|
||||
for candidate in \
|
||||
"$HOME/.steam/steam" \
|
||||
"$HOME/.local/share/Steam" \
|
||||
"$HOME/.var/app/com.valvesoftware.Steam/.steam/steam"; do
|
||||
if [ -d "$candidate/steamapps" ]; then
|
||||
echo "$candidate"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
STEAM_HOME=$(find_steam_home) || {
|
||||
echo "ERROR: Steam home not found. Install Steam and launch it once."
|
||||
exit 1
|
||||
}
|
||||
echo "Steam home: $STEAM_HOME"
|
||||
|
||||
# ── Verify / download Kyber installer ─────────────────────────────────────
|
||||
if [ ! -f "$KYBER_INSTALLER" ]; then
|
||||
# ── Check glibc version ────────────────────────────────────────────────────
|
||||
GLIBC=$(ldd --version 2>/dev/null | head -1 | grep -oP '\d+\.\d+$' || echo "0.0")
|
||||
GLIBC_MAJOR=$(echo "$GLIBC" | cut -d. -f1)
|
||||
GLIBC_MINOR=$(echo "$GLIBC" | cut -d. -f2)
|
||||
if [ "$GLIBC_MAJOR" -lt 2 ] || { [ "$GLIBC_MAJOR" -eq 2 ] && [ "$GLIBC_MINOR" -lt 38 ]; }; then
|
||||
echo "WARNING: glibc $GLIBC detected. The Kyber AppImage requires glibc 2.38+."
|
||||
echo " Ubuntu 22.04 ships glibc 2.35 — upgrade to Ubuntu 24.04 or use"
|
||||
echo " a newer distro. Continuing anyway in case your system has it..."
|
||||
echo ""
|
||||
echo "Kyber installer not found at: $KYBER_INSTALLER"
|
||||
echo "Downloading from kyber.gg API..."
|
||||
mkdir -p "$(dirname "$KYBER_INSTALLER")"
|
||||
KYBER_ZIP="/tmp/kyber-installer-$$.zip"
|
||||
KYBER_DL_URL="https://api.prod.kyber.gg/download/kyber-installer-win64.zip"
|
||||
if curl -L --progress-bar -o "$KYBER_ZIP" "$KYBER_DL_URL" && [ -s "$KYBER_ZIP" ]; then
|
||||
# Extract the installer .exe from the zip
|
||||
_exe=$(unzip -Z1 "$KYBER_ZIP" 2>/dev/null | grep -i '\.exe$' | head -1)
|
||||
if [ -z "$_exe" ]; then
|
||||
echo "ERROR: No .exe found inside the downloaded zip."
|
||||
rm -f "$KYBER_ZIP"
|
||||
exit 1
|
||||
fi
|
||||
unzip -p "$KYBER_ZIP" "$_exe" > "$KYBER_INSTALLER"
|
||||
rm -f "$KYBER_ZIP"
|
||||
echo "Extracted: $(basename "$_exe") → $KYBER_INSTALLER"
|
||||
fi
|
||||
|
||||
# ── Check/fix unprivileged user namespaces (bwrap requirement) ────────────
|
||||
echo "[0/3] Checking unprivileged user namespace support (required for Kyber)..."
|
||||
|
||||
USERNS_OK=true
|
||||
CLONE_VAL=$(sysctl -n kernel.unprivileged_userns_clone 2>/dev/null || echo "1")
|
||||
APPARMOR_VAL=$(sysctl -n kernel.apparmor_restrict_unprivileged_userns 2>/dev/null || echo "0")
|
||||
|
||||
if [ "$CLONE_VAL" != "1" ] || [ "$APPARMOR_VAL" != "0" ]; then
|
||||
USERNS_OK=false
|
||||
echo " WARNING: Unprivileged user namespaces are restricted."
|
||||
echo " Kyber uses bubblewrap (bwrap) which requires them."
|
||||
echo " Without this fix you will see: bwrap: setting up uid map: Permission denied"
|
||||
echo ""
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
echo " Applying fix now (running as root)..."
|
||||
sysctl -w kernel.unprivileged_userns_clone=1
|
||||
sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
|
||||
SYSCTL_FILE="/etc/sysctl.d/99-userns.conf"
|
||||
cat > "$SYSCTL_FILE" << 'SYSCTL'
|
||||
# Required for Kyber AppImage (bubblewrap sandbox)
|
||||
kernel.unprivileged_userns_clone = 1
|
||||
kernel.apparmor_restrict_unprivileged_userns = 0
|
||||
SYSCTL
|
||||
echo " Saved to $SYSCTL_FILE — will persist across reboots."
|
||||
USERNS_OK=true
|
||||
else
|
||||
echo " To fix, run these commands (requires sudo):"
|
||||
echo " sudo sysctl -w kernel.unprivileged_userns_clone=1"
|
||||
echo " sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0"
|
||||
echo ""
|
||||
echo "ERROR: Download failed."
|
||||
echo "Download the zip manually from https://kyber.gg, extract the .exe, then:"
|
||||
echo " $0 /path/to/KyberLauncher.exe"
|
||||
rm -f "$KYBER_ZIP"
|
||||
exit 1
|
||||
echo " To make permanent, create /etc/sysctl.d/99-userns.conf:"
|
||||
echo " echo 'kernel.unprivileged_userns_clone = 1' | sudo tee /etc/sysctl.d/99-userns.conf"
|
||||
echo " echo 'kernel.apparmor_restrict_unprivileged_userns = 0' | sudo tee -a /etc/sysctl.d/99-userns.conf"
|
||||
echo ""
|
||||
echo " Re-run this script with sudo to apply automatically."
|
||||
echo " Continuing with download regardless..."
|
||||
fi
|
||||
else
|
||||
echo " OK — unprivileged user namespaces are enabled."
|
||||
fi
|
||||
echo "Kyber installer: $KYBER_INSTALLER"
|
||||
echo ""
|
||||
|
||||
# ── Locate Proton Experimental ─────────────────────────────────────────────
|
||||
# Kyber's WebView2 is best supported on Proton Experimental (newest Wine +
|
||||
# the most complete WebView2/Edge compatibility shims). GE-Proton also works,
|
||||
# but Experimental tends to have the freshest fixes for Chromium sandboxing.
|
||||
PROTON_DIR=$(find "$STEAM_HOME/steamapps/common" -maxdepth 1 -type d \
|
||||
-iname "Proton Experimental" 2>/dev/null | head -1)
|
||||
if [ -z "$PROTON_DIR" ]; then
|
||||
PROTON_DIR=$(find "$STEAM_HOME/steamapps/common" -maxdepth 1 -type d \
|
||||
-iname "Proton*" 2>/dev/null | sort | tail -1)
|
||||
fi
|
||||
if [ -z "$PROTON_DIR" ] || [ ! -x "$PROTON_DIR/proton" ]; then
|
||||
echo ""
|
||||
echo "ERROR: Proton not found under $STEAM_HOME/steamapps/common."
|
||||
echo " In Steam → Settings → Compatibility, install Proton Experimental,"
|
||||
echo " then re-run this script."
|
||||
# ── Fetch latest release URL ───────────────────────────────────────────────
|
||||
echo "[1/3] Fetching latest Kyber Linux release..."
|
||||
API_URL="https://api.github.com/repos/${KYBER_REPO}/releases/latest"
|
||||
APPIMAGE_URL=$(curl -fsSL "$API_URL" \
|
||||
| python3 -c "
|
||||
import json, sys
|
||||
data = json.load(sys.stdin)
|
||||
assets = data.get('assets', [])
|
||||
for a in assets:
|
||||
url = a['browser_download_url']
|
||||
if url.endswith('.AppImage'):
|
||||
print(url)
|
||||
break
|
||||
" 2>/dev/null)
|
||||
|
||||
if [ -z "$APPIMAGE_URL" ]; then
|
||||
echo "ERROR: Could not fetch AppImage URL from GitHub."
|
||||
echo " Check: https://github.com/${KYBER_REPO}/releases"
|
||||
echo " Download the AppImage manually and run: chmod +x KyberLinuxPort*.AppImage && ./KyberLinuxPort*.AppImage"
|
||||
exit 1
|
||||
fi
|
||||
echo "Proton: $PROTON_DIR"
|
||||
|
||||
# ── Headless display check ─────────────────────────────────────────────────
|
||||
# WebView2 (and Kyber's Flutter UI) need a display to render to. On a headless
|
||||
# box with no X/Wayland session, Kyber's window has nowhere to draw.
|
||||
if [ -z "${DISPLAY:-}" ] && [ -z "${WAYLAND_DISPLAY:-}" ]; then
|
||||
echo ""
|
||||
echo "NOTE: No DISPLAY or WAYLAND_DISPLAY detected (headless box)."
|
||||
echo " For Steam Remote Play you need a virtual display so Steam can"
|
||||
echo " render. Options:"
|
||||
echo " - Configure your GPU driver's dummy/virtual display (recommended"
|
||||
echo " for hardware-encoded Remote Play), OR"
|
||||
echo " - Run this whole setup under Xvfb for the install step only:"
|
||||
echo " xvfb-run -a $0 $KYBER_INSTALLER"
|
||||
echo ""
|
||||
echo " Continuing the install (the prefix can be built headless), but you"
|
||||
echo " must have a real or virtual display when you actually launch Kyber."
|
||||
VERSION=$(echo "$APPIMAGE_URL" | grep -oP 'v[\d.a-z-]+' | head -1)
|
||||
echo " Latest: $VERSION"
|
||||
echo " URL: $APPIMAGE_URL"
|
||||
echo ""
|
||||
|
||||
# ── Download ───────────────────────────────────────────────────────────────
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
APPIMAGE_PATH="$INSTALL_DIR/KyberLinuxPort.AppImage"
|
||||
|
||||
CURRENT_VERSION=""
|
||||
if [ -f "$APPIMAGE_PATH.version" ]; then
|
||||
CURRENT_VERSION=$(cat "$APPIMAGE_PATH.version")
|
||||
fi
|
||||
|
||||
if [ -f "$APPIMAGE_PATH" ] && [ "$CURRENT_VERSION" = "$VERSION" ]; then
|
||||
echo "[2/3] Already up to date ($VERSION) — skipping download."
|
||||
else
|
||||
echo "[2/3] Downloading Kyber Linux AppImage ($VERSION)..."
|
||||
curl -L --progress-bar -o "$APPIMAGE_PATH" "$APPIMAGE_URL"
|
||||
chmod +x "$APPIMAGE_PATH"
|
||||
echo "$VERSION" > "$APPIMAGE_PATH.version"
|
||||
echo " Saved to: $APPIMAGE_PATH"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
# ── Desktop entry + bin symlink ────────────────────────────────────────────
|
||||
echo "[3/3] Installing desktop entry and launcher..."
|
||||
|
||||
mkdir -p "$(dirname "$DESKTOP_FILE")" "$HOME/.local/bin"
|
||||
|
||||
cat > "$DESKTOP_FILE" << EOF
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Kyber Launcher
|
||||
Comment=Community multiplayer for Star Wars Battlefront II (2017)
|
||||
Exec=${APPIMAGE_PATH}
|
||||
Icon=kyber
|
||||
Categories=Game;
|
||||
StartupNotify=true
|
||||
EOF
|
||||
|
||||
ln -sf "$APPIMAGE_PATH" "$BIN_LINK"
|
||||
update-desktop-database "$HOME/.local/share/applications" 2>/dev/null || true
|
||||
|
||||
echo ""
|
||||
echo "=== Kyber Setup Complete ==="
|
||||
echo ""
|
||||
echo "Launch Kyber:"
|
||||
echo " From terminal: kyber"
|
||||
echo " From app menu: search 'Kyber Launcher'"
|
||||
echo " Direct: $APPIMAGE_PATH"
|
||||
echo ""
|
||||
echo "Every time you want to play:"
|
||||
echo " 1. Open Steam (must be running for library validation)."
|
||||
echo " Do NOT click Play on SWBF2 in Steam — Kyber launches it."
|
||||
echo " 2. Launch Kyber and join or host a server."
|
||||
echo " 3. Kyber/Maxima will launch SWBF2 using its own bundled GE-Proton."
|
||||
echo " 4. Wait 1-3 minutes for the Frostbite loading screen."
|
||||
echo " 5. If SWBF2 appears but you can't click into it: press Alt+Tab or"
|
||||
echo " click its taskbar entry to bring it into focus."
|
||||
echo ""
|
||||
echo "First run (one-time):"
|
||||
echo " 1. Click 'EA Account' and log in with your EA credentials."
|
||||
echo " The browser opens to accounts.ea.com — log in there."
|
||||
echo " 2. Click 'Skip' on the Nexus Mods step (only needed for mods)."
|
||||
echo " 3. EA login is cached — you stay logged in across sessions."
|
||||
echo ""
|
||||
echo "If Kyber says 'Game Not Found':"
|
||||
echo " Click SET GAME FOLDER and run this to find the path:"
|
||||
echo " find ~/.steam/steam/steamapps -name 'starwarsbattlefrontii.exe' 2>/dev/null | head -1 | xargs dirname"
|
||||
echo " Paste that path into the SET GAME FOLDER dialog. Kyber remembers it."
|
||||
echo ""
|
||||
echo "Hosting a private server with bots:"
|
||||
echo " HOST → pick maps/modes → set a name and PASSWORD → Start Server."
|
||||
echo " Share the server name + password with friends (they search by name in HOME)."
|
||||
echo " Bot count: HOST panel → AUTOPLAYERS → set BOTS TEAM 1 and BOTS TEAM 2"
|
||||
echo " (e.g. 4 each) → click UPDATE SERVER."
|
||||
echo " Bot difficulty: use the BOT DIFFICULTY slider (RECRUIT / OFFICER / KNIGHT / MASTER)."
|
||||
echo ""
|
||||
if [ "$USERNS_OK" = "false" ]; then
|
||||
echo "IMPORTANT: Fix unprivileged user namespaces before launching Kyber:"
|
||||
echo " sudo sysctl -w kernel.unprivileged_userns_clone=1"
|
||||
echo " sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0"
|
||||
echo " Or re-run this script with sudo to apply automatically."
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# ── Build Kyber Wine prefix and run the installer ──────────────────────────
|
||||
echo ""
|
||||
echo "[1/7] Installing Kyber into Wine prefix..."
|
||||
|
||||
KYBER_PFX="$STEAM_HOME/steamapps/compatdata/$KYBER_COMPAT_ID"
|
||||
OLD_PFX="$STEAM_HOME/steamapps/compatdata/kyber"
|
||||
|
||||
# Migrate old 'kyber' prefix to the numeric appid directory Steam expects
|
||||
if [ -d "$OLD_PFX" ] && [ ! -d "$KYBER_PFX" ]; then
|
||||
echo " Migrating prefix: compatdata/kyber → compatdata/$KYBER_COMPAT_ID"
|
||||
mv "$OLD_PFX" "$KYBER_PFX"
|
||||
elif [ -d "$OLD_PFX" ] && [ -d "$KYBER_PFX" ]; then
|
||||
echo " Removing old compatdata/kyber (numeric prefix already exists)..."
|
||||
rm -rf "$OLD_PFX"
|
||||
fi
|
||||
|
||||
export STEAM_COMPAT_DATA_PATH="$KYBER_PFX"
|
||||
export STEAM_COMPAT_CLIENT_INSTALL_PATH="$STEAM_HOME"
|
||||
export PROTON_NO_ESYNC=1
|
||||
|
||||
# Check if Kyber is already installed — skip the installer if so.
|
||||
KYBER_EXE_PATH=$(find "$KYBER_PFX/pfx" -iname "kyber_launcher.exe" 2>/dev/null | head -1)
|
||||
if [ -n "$KYBER_EXE_PATH" ]; then
|
||||
echo " Kyber.exe already present — skipping installer."
|
||||
else
|
||||
mkdir -p "$KYBER_PFX"
|
||||
# Kill any leftover Wine/Proton processes from a previous attempt.
|
||||
pkill -9 -f "compatdata/$KYBER_COMPAT_ID" 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
echo " Running installer (silent)..."
|
||||
# /S = NSIS silent flag; if Kyber's installer ignores it a GUI appears.
|
||||
"$PROTON_DIR/proton" run "$KYBER_INSTALLER" /S 2>/dev/null || \
|
||||
"$PROTON_DIR/proton" run "$KYBER_INSTALLER" 2>/dev/null || true
|
||||
|
||||
sleep 5
|
||||
|
||||
KYBER_EXE_PATH=$(find "$KYBER_PFX/pfx" -iname "kyber_launcher.exe" 2>/dev/null | head -1)
|
||||
if [ -z "$KYBER_EXE_PATH" ]; then
|
||||
echo ""
|
||||
echo "WARNING: Kyber.exe not found after installation."
|
||||
echo " Default install path assumed; continuing."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Resolve Windows-style paths for the shortcut
|
||||
if [ -n "$KYBER_EXE_PATH" ]; then
|
||||
rel=$(echo "$KYBER_EXE_PATH" | sed "s|.*/pfx/drive_c/||")
|
||||
KYBER_EXE_WIN="C:\\$(echo "$rel" | sed 's|/|\\|g')"
|
||||
dir_rel=$(dirname "$rel")
|
||||
KYBER_START_DIR="C:\\$(echo "$dir_rel" | sed 's|/|\\|g')\\"
|
||||
else
|
||||
KYBER_EXE_WIN='C:\Program Files (x86)\KYBER Launcher\kyber_launcher.exe'
|
||||
KYBER_START_DIR='C:\Program Files (x86)\KYBER Launcher\'
|
||||
fi
|
||||
echo " Windows path: $KYBER_EXE_WIN"
|
||||
|
||||
# ── Ensure WebView2 Evergreen runtime is installed ─────────────────────────
|
||||
echo ""
|
||||
echo "[2/7] Verifying WebView2 runtime in the Kyber prefix..."
|
||||
|
||||
# The Evergreen runtime lives here once installed.
|
||||
WV2_FOUND=$(find "$KYBER_PFX/pfx/drive_c" -iname "msedgewebview2.exe" 2>/dev/null | head -1)
|
||||
if [ -n "$WV2_FOUND" ]; then
|
||||
echo " WebView2 runtime present:"
|
||||
echo " $WV2_FOUND"
|
||||
else
|
||||
echo " WebView2 runtime NOT found — installing..."
|
||||
|
||||
# Prefer winetricks if available — it handles the prefix env automatically
|
||||
# and uses a cached offline installer so no Wine-internal network call needed.
|
||||
if command -v winetricks >/dev/null 2>&1; then
|
||||
echo " Using winetricks to install webview2..."
|
||||
WINEPREFIX="$KYBER_PFX/pfx" \
|
||||
WINE="$PROTON_DIR/files/lib/wine/x86_64-unix/wine64" \
|
||||
winetricks -q webview2 || true
|
||||
else
|
||||
# Download the Evergreen STANDALONE (offline) installer — linkid=2135547.
|
||||
# The bootstrapper (linkid=2124703) requires a second download from inside
|
||||
# Wine which reliably fails. The standalone is ~150 MB but self-contained.
|
||||
echo " Downloading WebView2 standalone installer (~150 MB)..."
|
||||
WV2_STANDALONE="/tmp/WebView2RuntimeInstaller_$$.exe"
|
||||
WV2_URL="https://go.microsoft.com/fwlink/p/?LinkId=2135547"
|
||||
if curl -L --progress-bar -o "$WV2_STANDALONE" "$WV2_URL" && [ -s "$WV2_STANDALONE" ]; then
|
||||
"$PROTON_DIR/proton" run "$WV2_STANDALONE" /silent /install || true
|
||||
rm -f "$WV2_STANDALONE"
|
||||
else
|
||||
echo " WARNING: Could not download WebView2 standalone installer."
|
||||
rm -f "$WV2_STANDALONE"
|
||||
fi
|
||||
fi
|
||||
|
||||
sleep 5
|
||||
WV2_FOUND=$(find "$KYBER_PFX/pfx/drive_c" -iname "msedgewebview2.exe" 2>/dev/null | head -1)
|
||||
if [ -n "$WV2_FOUND" ]; then
|
||||
echo " WebView2 runtime installed:"
|
||||
echo " $WV2_FOUND"
|
||||
else
|
||||
echo ""
|
||||
echo " WARNING: WebView2 runtime still not found after install attempt."
|
||||
echo " Install winetricks and re-run, or install manually:"
|
||||
echo " sudo apt install winetricks"
|
||||
echo " $0"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Install cmd shim + IFEO registry hook ─────────────────────────────────
|
||||
# Wine's built-in cmd.exe crashes (exit 0xC0000005) when Kyber calls:
|
||||
# cmd /c start "" "<EA auth URL>"
|
||||
#
|
||||
# FIX: Use Windows "Image File Execution Options" (IFEO) to intercept any
|
||||
# cmd.exe launch at the registry level. IFEO survives Proton's prefix-setup
|
||||
# phase (which overwrites system32/cmd.exe on every game launch), because it
|
||||
# lives in the registry — not the filesystem.
|
||||
#
|
||||
# Mechanism:
|
||||
# HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\
|
||||
# Image File Execution Options\cmd.exe → Debugger = C:\shim\kyber_cmd.exe
|
||||
#
|
||||
# Wine reads this key at CreateProcess time and runs our shim instead of
|
||||
# cmd.exe. The shim scans its arguments for an http URL, writes it to
|
||||
# C:\kyber_oauth_url.txt, and exits 0. A Linux-side watcher reads the file
|
||||
# and calls xdg-open to open the URL in the system browser.
|
||||
#
|
||||
# The shim is placed in drive_c/shim/ — a directory Proton never touches.
|
||||
echo ""
|
||||
echo "[3/7] Installing cmd shim + IFEO registry hook for OAuth login..."
|
||||
|
||||
SHIM_DIR="$KYBER_PFX/pfx/drive_c/shim"
|
||||
SHIM_EXE_PATH="$SHIM_DIR/kyber_cmd.exe"
|
||||
SYSTEM_REG="$KYBER_PFX/pfx/system.reg"
|
||||
|
||||
IFEO_ALREADY=0
|
||||
if grep -q "Image File Execution Options\\\\cmd.exe" "$SYSTEM_REG" 2>/dev/null; then
|
||||
IFEO_ALREADY=1
|
||||
fi
|
||||
|
||||
if [ -f "$SHIM_EXE_PATH" ] && [ "$IFEO_ALREADY" = "1" ]; then
|
||||
echo " cmd shim + IFEO already installed."
|
||||
else
|
||||
# Compile the shim if not present
|
||||
if [ ! -f "$SHIM_EXE_PATH" ]; then
|
||||
if ! command -v x86_64-w64-mingw32-gcc >/dev/null 2>&1; then
|
||||
echo " Installing mingw-w64..."
|
||||
sudo apt-get install -y mingw-w64 || {
|
||||
echo " WARNING: mingw-w64 install failed. Install manually:"
|
||||
echo " sudo apt install mingw-w64"
|
||||
echo " Then re-run this script."
|
||||
}
|
||||
fi
|
||||
|
||||
if command -v x86_64-w64-mingw32-gcc >/dev/null 2>&1; then
|
||||
SHIM_C="/tmp/kyber_cmd_shim_$$.c"
|
||||
SHIM_EXE_TMP="/tmp/kyber_cmd_shim_$$.exe"
|
||||
cat > "$SHIM_C" << 'CEOF'
|
||||
#include <windows.h>
|
||||
static void write_url(LPCWSTR url) {
|
||||
HANDLE h = CreateFileW(L"C:\\kyber_oauth_url.txt",
|
||||
GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (h == INVALID_HANDLE_VALUE) return;
|
||||
int n = WideCharToMultiByte(CP_UTF8, 0, url, -1, NULL, 0, NULL, NULL);
|
||||
char *buf = (char*)HeapAlloc(GetProcessHeap(), 0, n + 1);
|
||||
WideCharToMultiByte(CP_UTF8, 0, url, -1, buf, n, NULL, NULL);
|
||||
DWORD w; WriteFile(h, buf, n - 1, &w, NULL);
|
||||
HeapFree(GetProcessHeap(), 0, buf);
|
||||
CloseHandle(h);
|
||||
}
|
||||
int WINAPI mainCRTStartup(void) {
|
||||
/* IFEO prepends our exe name before the real command line, so skip argv[0]
|
||||
and argv[1] (the debuggee path Wine adds). Scan remaining args for URL. */
|
||||
int argc;
|
||||
LPWSTR *argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
for (int i = 1; i < argc; i++) {
|
||||
LPCWSTR a = argv[i];
|
||||
if ((a[0]=='h'||a[0]=='H') && (a[1]=='t'||a[1]=='T') &&
|
||||
(a[2]=='t'||a[2]=='T') && (a[3]=='p'||a[3]=='P')) {
|
||||
write_url(a);
|
||||
LocalFree(argv);
|
||||
ExitProcess(0);
|
||||
}
|
||||
}
|
||||
LocalFree(argv);
|
||||
ExitProcess(0);
|
||||
}
|
||||
CEOF
|
||||
mkdir -p "$SHIM_DIR"
|
||||
if x86_64-w64-mingw32-gcc -O2 -ffreestanding -nostdlib \
|
||||
-mno-stack-arg-probe \
|
||||
-e mainCRTStartup -o "$SHIM_EXE_TMP" "$SHIM_C" \
|
||||
-lkernel32 -lshell32; then
|
||||
cp "$SHIM_EXE_TMP" "$SHIM_EXE_PATH"
|
||||
echo " Shim compiled and placed: $SHIM_EXE_PATH ($(stat -c%s "$SHIM_EXE_PATH") bytes)"
|
||||
else
|
||||
echo " WARNING: cmd shim compile failed — login may not work."
|
||||
fi
|
||||
rm -f "$SHIM_C" "$SHIM_EXE_TMP"
|
||||
else
|
||||
echo " WARNING: mingw-w64 not found. Install it and re-run:"
|
||||
echo " sudo apt install mingw-w64"
|
||||
fi
|
||||
else
|
||||
echo " Shim already compiled."
|
||||
fi
|
||||
|
||||
# Write IFEO registry key to system.reg
|
||||
# system.reg uses \\ for path separators in key name brackets.
|
||||
if [ "$IFEO_ALREADY" = "0" ] && [ -f "$SYSTEM_REG" ]; then
|
||||
echo "" >> "$SYSTEM_REG"
|
||||
echo "[Software\\\\Microsoft\\\\Windows NT\\\\CurrentVersion\\\\Image File Execution Options\\\\cmd.exe]" >> "$SYSTEM_REG"
|
||||
echo '"Debugger"="C:\\\\shim\\\\kyber_cmd.exe"' >> "$SYSTEM_REG"
|
||||
echo " IFEO registry key written to system.reg."
|
||||
elif [ ! -f "$SYSTEM_REG" ]; then
|
||||
echo " WARNING: system.reg not found — IFEO key not written."
|
||||
echo " The prefix may not exist yet. Run the installer first (step 1)."
|
||||
else
|
||||
echo " IFEO registry key already present."
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── OAuth watcher ──────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
echo "[4/7] Installing OAuth watcher (opens EA login in your browser)..."
|
||||
|
||||
URL_FILE="$KYBER_PFX/pfx/drive_c/kyber_oauth_url.txt"
|
||||
WATCHER="$HOME/.local/bin/kyber-oauth-watcher.sh"
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
|
||||
cat > "$WATCHER" << WEOF
|
||||
#!/bin/bash
|
||||
# Watches for Kyber's OAuth URL and opens it in the system browser.
|
||||
URL_FILE="$URL_FILE"
|
||||
echo "[kyber-watcher] Started. Watching \$URL_FILE"
|
||||
rm -f "\$URL_FILE"
|
||||
while true; do
|
||||
if [ -f "\$URL_FILE" ]; then
|
||||
URL=\$(cat "\$URL_FILE")
|
||||
rm -f "\$URL_FILE"
|
||||
if [[ "\$URL" == http* ]]; then
|
||||
echo "[kyber-watcher] Opening: \$URL"
|
||||
xdg-open "\$URL"
|
||||
fi
|
||||
fi
|
||||
sleep 0.5
|
||||
done
|
||||
WEOF
|
||||
chmod +x "$WATCHER"
|
||||
|
||||
# Install as a systemd user service so it is always ready when Kyber runs
|
||||
SVCDIR="$HOME/.config/systemd/user"
|
||||
mkdir -p "$SVCDIR"
|
||||
cat > "$SVCDIR/kyber-oauth-watcher.service" << SEOF
|
||||
[Unit]
|
||||
Description=Kyber EA OAuth URL watcher
|
||||
After=graphical-session.target
|
||||
|
||||
[Service]
|
||||
ExecStart=$WATCHER
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
SEOF
|
||||
|
||||
if systemctl --user daemon-reload 2>/dev/null && \
|
||||
systemctl --user enable --now kyber-oauth-watcher.service 2>/dev/null; then
|
||||
echo " Watcher service enabled and started."
|
||||
echo " (It will auto-start on login from now on.)"
|
||||
else
|
||||
echo " NOTE: systemd user service could not be enabled."
|
||||
echo " Start the watcher manually before clicking Login in Kyber:"
|
||||
echo " $WATCHER &"
|
||||
fi
|
||||
|
||||
# ── Add Kyber as a non-Steam shortcut ──────────────────────────────────────
|
||||
echo ""
|
||||
echo "[5/7] Adding Kyber as a non-Steam shortcut..."
|
||||
|
||||
STEAM_UID=$(ls "$STEAM_HOME/userdata/" 2>/dev/null | grep -E '^[0-9]+$' | head -1)
|
||||
if [ -z "$STEAM_UID" ]; then
|
||||
echo " WARNING: No Steam userdata found — sign in to Steam once, then re-run."
|
||||
echo " Skipping shortcut + compat mapping."
|
||||
SKIP_STEAM_CFG=1
|
||||
else
|
||||
SHORTCUTS_DIR="$STEAM_HOME/userdata/$STEAM_UID/config"
|
||||
SHORTCUTS_FILE="$SHORTCUTS_DIR/shortcuts.vdf"
|
||||
mkdir -p "$SHORTCUTS_DIR"
|
||||
if [ -f "$SHORTCUTS_FILE" ] && [ ! -w "$SHORTCUTS_FILE" ]; then
|
||||
echo " Fixing permissions on shortcuts.vdf..."
|
||||
chmod 644 "$SHORTCUTS_FILE" || {
|
||||
echo " WARNING: Cannot write $SHORTCUTS_FILE — run:"
|
||||
echo " chmod 644 $SHORTCUTS_FILE"
|
||||
echo " then re-run this script."
|
||||
SKIP_STEAM_CFG=1
|
||||
}
|
||||
fi
|
||||
[ -f "$SHORTCUTS_FILE" ] && cp "$SHORTCUTS_FILE" "$SHORTCUTS_FILE.bak"
|
||||
|
||||
python3 - "$SHORTCUTS_FILE" "$KYBER_APPID" "$KYBER_EXE_WIN" "$KYBER_START_DIR" << 'PYEOF'
|
||||
import sys, struct, os
|
||||
|
||||
def s(key, value): # VDF string field
|
||||
return b'\x01' + key.encode() + b'\x00' + value.encode() + b'\x00'
|
||||
def i(key, value): # VDF int field
|
||||
return b'\x02' + key.encode() + b'\x00' + struct.pack('<I', value)
|
||||
|
||||
shortcuts_file, appid_s, exe_win, start_dir = sys.argv[1:5]
|
||||
appid = int(appid_s)
|
||||
|
||||
existing = b''
|
||||
idx = 0
|
||||
if os.path.exists(shortcuts_file):
|
||||
try:
|
||||
with open(shortcuts_file, 'rb') as f:
|
||||
raw = f.read()
|
||||
head = b'\x00shortcuts\x00'
|
||||
if raw.startswith(head) and raw.endswith(b'\x08\x08'):
|
||||
existing = raw[len(head):-2]
|
||||
# Skip if Kyber Launcher entry already present
|
||||
if b'Kyber Launcher' in existing:
|
||||
print(" Kyber Launcher already in shortcuts.vdf — skipping.")
|
||||
sys.exit(0)
|
||||
idx = existing.count(b'\x01appid\x00')
|
||||
except Exception:
|
||||
existing = b''
|
||||
idx = 0
|
||||
|
||||
entry = b'\x00' + str(idx).encode() + b'\x00'
|
||||
entry += s('appid', str(appid))
|
||||
entry += s('AppName', 'Kyber Launcher')
|
||||
entry += s('Exe', f'"{exe_win}"')
|
||||
entry += s('StartDir', f'"{start_dir}"')
|
||||
entry += s('icon', '')
|
||||
entry += s('ShortcutPath', '')
|
||||
entry += s('LaunchOptions', '')
|
||||
entry += i('IsHidden', 0)
|
||||
entry += i('AllowDesktopConfig', 1)
|
||||
entry += i('AllowOverlay', 1)
|
||||
entry += i('OpenVR', 0)
|
||||
entry += i('Devkit', 0)
|
||||
entry += s('DevkitGameID', '')
|
||||
entry += i('LastPlayTime', 0)
|
||||
entry += b'\x08\x08'
|
||||
|
||||
data = b'\x00shortcuts\x00' + existing + entry + b'\x08'
|
||||
with open(shortcuts_file, 'wb') as f:
|
||||
f.write(data)
|
||||
print(f" shortcuts.vdf written ({len(data)} bytes, entry index {idx})")
|
||||
PYEOF
|
||||
fi
|
||||
|
||||
# ── CompatToolMapping — force Proton Experimental for the Kyber shortcut ────
|
||||
echo ""
|
||||
echo "[6/7] Forcing Proton Experimental for the Kyber shortcut..."
|
||||
if [ "${SKIP_STEAM_CFG:-0}" != "1" ]; then
|
||||
CFG_FILE="$STEAM_HOME/config/config.vdf"
|
||||
if [ -f "$CFG_FILE" ]; then
|
||||
cp "$CFG_FILE" "$CFG_FILE.bak"
|
||||
python3 - "$CFG_FILE" "$KYBER_APPID" << 'PYEOF'
|
||||
import sys, re
|
||||
cfg_file, appid = sys.argv[1], sys.argv[2]
|
||||
with open(cfg_file) as f:
|
||||
content = f.read()
|
||||
if f'"{appid}"' in content:
|
||||
print(f" CompatToolMapping for {appid} already present — skipping.")
|
||||
sys.exit(0)
|
||||
entry = (
|
||||
f'\t\t\t\t"{appid}"\n\t\t\t\t{{\n'
|
||||
f'\t\t\t\t\t"name"\t\t"proton_experimental"\n'
|
||||
f'\t\t\t\t\t"config"\t\t""\n'
|
||||
f'\t\t\t\t\t"Priority"\t\t"250"\n'
|
||||
f'\t\t\t\t}}\n'
|
||||
)
|
||||
new = re.sub(r'("CompatToolMapping"\s*\n\s*\{)', r'\1\n' + entry, content)
|
||||
if new == content:
|
||||
print(" WARNING: CompatToolMapping block not found.")
|
||||
print(" Set Proton Experimental for Kyber manually: right-click the")
|
||||
print(" Kyber shortcut in Steam → Properties → Compatibility.")
|
||||
sys.exit(0)
|
||||
with open(cfg_file, 'w') as f:
|
||||
f.write(new)
|
||||
print(f" CompatToolMapping → proton_experimental for appid {appid}")
|
||||
PYEOF
|
||||
else
|
||||
echo " WARNING: config.vdf not found. Set Proton Experimental manually"
|
||||
echo " in the Kyber shortcut's Properties → Compatibility."
|
||||
fi
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "[7/7] Done."
|
||||
echo ""
|
||||
echo "=== Kyber Setup Complete (native Linux) ==="
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. RESTART Steam so it picks up the new shortcut and compat mapping."
|
||||
echo " 2. The OAuth watcher is already running in the background."
|
||||
echo " (Confirm: systemctl --user status kyber-oauth-watcher)"
|
||||
echo " 3. Launch 'Kyber Launcher' from your Steam Library."
|
||||
echo " 4. Click Login. Kyber calls cmd /c start with the EA auth URL."
|
||||
echo " The cmd shim intercepts it and writes it to a file. The watcher"
|
||||
echo " picks it up and opens it in your browser via xdg-open."
|
||||
echo " 5. Log in on the EA page (email / Steam / 2FA as usual)."
|
||||
echo " 6. EA redirects to 127.0.0.1:<port>/?code=... — Kyber's loopback"
|
||||
echo " server catches the auth code and login completes."
|
||||
echo " The browser tab will show 'OK' or connection-refused — both normal."
|
||||
echo ""
|
||||
echo "Streaming from a headless box:"
|
||||
echo " Use Steam Remote Play — install the Steam Link app on your client,"
|
||||
echo " it discovers this host over your network."
|
||||
echo ""
|
||||
echo "Troubleshooting:"
|
||||
echo " Browser never opens after clicking Login:"
|
||||
echo " Check watcher is running: systemctl --user status kyber-oauth-watcher"
|
||||
echo " If not: $HOME/.local/bin/kyber-oauth-watcher.sh &"
|
||||
echo ""
|
||||
echo " Browser opens but login loops back to EA sign-in page:"
|
||||
echo " Kyber's loopback server timed out. Close Kyber, reopen it,"
|
||||
echo " then click Login and log in quickly."
|
||||
echo ""
|
||||
echo " 'cmd shim' missing (mingw-w64 was not installed):"
|
||||
echo " sudo apt install mingw-w64 && $0"
|
||||
echo "SWBF2 must be installed via Steam (AppID 1237950)."
|
||||
echo "To update Kyber later, re-run this script."
|
||||
|
||||
Reference in New Issue
Block a user