From 40891cba5bbf70f61544f5a70eecd50095d51b3c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Jun 2026 01:00:30 +0000 Subject: [PATCH 1/6] fix(kyber): correct IFEO backslash count and add shim validity check system.reg values use \\ for one backslash, so C:\shim\kyber_cmd.exe must be written as C:\\shim\\kyber_cmd.exe. The previous script wrote C:\\\\shim\\\\kyber_cmd.exe (4 backslashes = two literal backslashes), which Wine couldn't resolve. Also detect when the shim slot contains Wine's cmd.exe (122231 bytes) left behind by a failed earlier attempt, and recompile automatically. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs --- scripts/setup-kyber-linux.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scripts/setup-kyber-linux.sh b/scripts/setup-kyber-linux.sh index abc0489..098356f 100755 --- a/scripts/setup-kyber-linux.sh +++ b/scripts/setup-kyber-linux.sh @@ -293,7 +293,14 @@ fi if [ -f "$SHIM_EXE_PATH" ] && [ "$IFEO_ALREADY" = "1" ]; then echo " cmd shim + IFEO already installed." else - # Compile the shim if not present + # Compile the shim if not present or if it is the wrong file + # (a failed earlier run may have left Wine's cmd.exe there: 122231 bytes) + SHIM_SIZE=$(stat -c%s "$SHIM_EXE_PATH" 2>/dev/null || echo 0) + if [ "$SHIM_SIZE" -gt 100000 ] && [ -f "$SHIM_EXE_PATH" ]; then + echo " Shim file looks wrong ($SHIM_SIZE bytes — expected ~10KB). Replacing..." + rm -f "$SHIM_EXE_PATH" + fi + if [ ! -f "$SHIM_EXE_PATH" ]; then if ! command -v x86_64-w64-mingw32-gcc >/dev/null 2>&1; then echo " Installing mingw-w64..." @@ -361,8 +368,8 @@ CEOF # 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 '[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." From 6eb947ae6d3ea405edc0aaea017021f71059833f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 24 Jun 2026 03:09:42 +0000 Subject: [PATCH 2/6] =?UTF-8?q?fix(kyber):=20replace=20Proton=20cmd.exe=20?= =?UTF-8?q?directly=20=E2=80=94=20the=20only=20working=20approach?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After extensive testing, Wine/Proton loads cmd.exe exclusively from Proton's own installation (files/lib/wine/x86_64-windows/cmd.exe). Replacing the prefix system32 copy, IFEO registry keys, WINEDLLOVERRIDES, and Wine DllOverrides registry entries all have no effect — Proton's copy always takes precedence. The working fix: compile a tiny shim (GetCommandLineA scan for http URLs, write to C:\kyber_oauth_url.txt, ExitProcess(0)) and replace Proton's cmd.exe directly, backing up the original as cmd.exe.bak. The script detects size (shim ~9KB vs real cmd.exe ~1.2MB) to avoid double-replacing. Note for users: re-run setup-kyber-linux.sh after any Proton Experimental update since Steam restores the original cmd.exe on update. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs --- scripts/setup-kyber-linux.sh | 182 +++++++++++++++++------------------ 1 file changed, 86 insertions(+), 96 deletions(-) diff --git a/scripts/setup-kyber-linux.sh b/scripts/setup-kyber-linux.sh index 098356f..a5b7418 100755 --- a/scripts/setup-kyber-linux.sh +++ b/scripts/setup-kyber-linux.sh @@ -14,14 +14,16 @@ # 4. Kyber's HTTP server catches the code and exchanges it for tokens # # Problem: Wine's cmd.exe crashes with STATUS_ACCESS_VIOLATION (0xC0000005) -# when called as cmd /c start "" "". This blocks the login entirely. +# when called with a long EA auth URL. This blocks login entirely. # -# 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. +# Fix: Replace Proton's own cmd.exe (files/lib/wine/x86_64-windows/cmd.exe) +# with a tiny shim that captures any http URL from its command line, writes +# it to C:\kyber_oauth_url.txt, and exits 0. A Linux-side watcher calls +# xdg-open on that file to open the browser. Wine loads cmd.exe from Proton's +# installation, not the prefix — prefix replacements, IFEO, and +# WINEDLLOVERRIDES all have no effect, so Proton's copy must be replaced. +# +# After a Proton Experimental update, cmd.exe is restored; re-run this script. # # ── Why NOT to run Kyber inside Wolf / Games-on-Whales ───────────────────── # @@ -259,124 +261,109 @@ else fi fi -# ── Install cmd shim + IFEO registry hook ───────────────────────────────── +# ── Install cmd shim ────────────────────────────────────────────────────── # Wine's built-in cmd.exe crashes (exit 0xC0000005) when Kyber calls: # cmd /c start "" "" # -# 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. +# Root cause: Wine loads cmd.exe from Proton's OWN installation directory +# files/lib/wine/x86_64-windows/cmd.exe +# NOT from the prefix system32. Replacing the prefix copy, using IFEO registry +# keys, or WINEDLLOVERRIDES all have no effect — Proton's copy always wins. # -# Mechanism: -# HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ -# Image File Execution Options\cmd.exe → Debugger = C:\shim\kyber_cmd.exe +# Fix: replace Proton's cmd.exe with a tiny shim that scans its command line +# for an http URL, writes it to C:\kyber_oauth_url.txt, then exits 0. A +# Linux-side watcher calls xdg-open on that URL to open the system browser. +# The original is backed up as cmd.exe.bak next to it. # -# 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. +# After a Proton update the shim will be overwritten; re-run this script. echo "" -echo "[3/7] Installing cmd shim + IFEO registry hook for OAuth login..." +echo "[3/7] Installing cmd shim 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" +PROTON_CMD="$PROTON_DIR/files/lib/wine/x86_64-windows/cmd.exe" +PROTON_CMD_BAK="${PROTON_CMD}.bak" -IFEO_ALREADY=0 -if grep -q "Image File Execution Options\\\\cmd.exe" "$SYSTEM_REG" 2>/dev/null; then - IFEO_ALREADY=1 -fi +# Detect if Proton's cmd.exe is already our shim (small) or the original (large) +PROTON_CMD_SIZE=$(stat -c%s "$PROTON_CMD" 2>/dev/null || echo 0) -if [ -f "$SHIM_EXE_PATH" ] && [ "$IFEO_ALREADY" = "1" ]; then - echo " cmd shim + IFEO already installed." -else - # Compile the shim if not present or if it is the wrong file - # (a failed earlier run may have left Wine's cmd.exe there: 122231 bytes) - SHIM_SIZE=$(stat -c%s "$SHIM_EXE_PATH" 2>/dev/null || echo 0) - if [ "$SHIM_SIZE" -gt 100000 ] && [ -f "$SHIM_EXE_PATH" ]; then - echo " Shim file looks wrong ($SHIM_SIZE bytes — expected ~10KB). Replacing..." - rm -f "$SHIM_EXE_PATH" +compile_shim() { + 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 — login may not work." + echo " sudo apt install mingw-w64 && $0" + return 1 + } fi - - 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' + local SHIM_C="/tmp/kyber_cmd_shim_$$.c" + local SHIM_OUT="/tmp/kyber_cmd_shim_$$.exe" + cat > "$SHIM_C" << 'CEOF' #include -static void write_url(LPCWSTR url) { - HANDLE h = CreateFileW(L"C:\\kyber_oauth_url.txt", +static void write_url(const char *p, int len) { + HANDLE h = CreateFileA("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); + DWORD w; WriteFile(h, p, len, &w, NULL); 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); + const char *cl = GetCommandLineA(); + while (*cl) { + if ((cl[0]=='h'||cl[0]=='H') && cl[1]=='t' && cl[2]=='t' && cl[3]=='p') { + const char *end = cl; + while (*end && *end != ' ' && *end != '"') end++; + write_url(cl, (int)(end - cl)); ExitProcess(0); } + cl++; } - 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 + mkdir -p "$SHIM_DIR" + if x86_64-w64-mingw32-gcc -O2 -ffreestanding -nostdlib \ + -mno-stack-arg-probe \ + -e mainCRTStartup -o "$SHIM_OUT" "$SHIM_C" \ + -lkernel32; then + cp "$SHIM_OUT" "$SHIM_EXE_PATH" + echo " Shim compiled: $(stat -c%s "$SHIM_EXE_PATH") bytes" else - echo " Shim already compiled." + echo " WARNING: cmd shim compile failed — login may not work." + rm -f "$SHIM_C" "$SHIM_OUT" + return 1 fi + rm -f "$SHIM_C" "$SHIM_OUT" +} - # 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)." +# Compile shim if missing or obviously wrong size (>100KB = real cmd.exe) +SHIM_SIZE=$(stat -c%s "$SHIM_EXE_PATH" 2>/dev/null || echo 0) +if [ "$SHIM_SIZE" -gt 100000 ]; then + echo " Stale shim detected ($SHIM_SIZE bytes). Recompiling..." + rm -f "$SHIM_EXE_PATH" + SHIM_SIZE=0 +fi +if [ "$SHIM_SIZE" -eq 0 ]; then + compile_shim || true +fi + +# Replace Proton's cmd.exe if it is not already our shim +if [ -f "$SHIM_EXE_PATH" ]; then + if [ "$PROTON_CMD_SIZE" -gt 100000 ]; then + # Original: back it up and replace + cp "$PROTON_CMD" "$PROTON_CMD_BAK" 2>/dev/null || true + chmod u+w "$PROTON_CMD" + cp "$SHIM_EXE_PATH" "$PROTON_CMD" + chmod u-w "$PROTON_CMD" + echo " Proton cmd.exe replaced with shim." + echo " (Backup: $PROTON_CMD_BAK)" + echo " NOTE: Re-run this script after a Proton Experimental update." else - echo " IFEO registry key already present." + echo " Proton cmd.exe already replaced ($(stat -c%s "$PROTON_CMD") bytes)." fi +else + echo " WARNING: shim not compiled. Install mingw-w64 and re-run:" + echo " sudo apt install mingw-w64 && $0" fi # ── OAuth watcher ────────────────────────────────────────────────────────── @@ -582,3 +569,6 @@ 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 "" +echo " After a Proton Experimental update:" +echo " Re-run $0 to replace the restored cmd.exe with the shim again." From 390546e9f17e31f37bdbf33a742927fc77c6e266 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 14:09:09 +0000 Subject: [PATCH 3/6] Replace Wine-based Kyber setup with native Linux AppImage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows kyber_launcher.exe approach under Wine/Proton has two unfixable problems on Linux: Wine's cmd.exe crashes with STATUS_ACCESS_VIOLATION on the long EA OAuth URL, and EA's auth callback uses the eadesktop:// URI scheme which has no Linux handler. The correct solution is the native Linux port AppImage maintained at github.com/simonlinuxcraft/kyber-linuxport-unofficial. It bundles Maxima (open-source EA Desktop replacement) and handles OAuth natively — no shims, no watchers, no per-Proton-update maintenance. Script now: fetches latest release from GitHub API, downloads AppImage, installs desktop entry and ~/.local/bin/kyber symlink. README: add Gaming scripts section documenting Kyber setup, private server hosting with bots, requirements, and what does not work. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs --- README.md | 50 +++ scripts/setup-kyber-linux.sh | 647 +++++++---------------------------- 2 files changed, 173 insertions(+), 524 deletions(-) diff --git a/README.md b/README.md index 55e4f51..0bd98d0 100644 --- a/README.md +++ b/README.md @@ -137,3 +137,53 @@ 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`. + +**First run:** +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. Browse servers on **HOME** or create one under **HOST** + +**Hosting a private server with bots:** +- HOST → pick maps/modes → set a **name** and **PASSWORD** → Start Server +- Share the name + password with friends; they search by name in HOME +- SWBF2 fills empty slots with AI automatically — no separate bot setting + +**Requirements:** +- SWBF2 (Steam AppID 1237950) installed with GE-Proton (recommended over + Proton Experimental for game stability) +- glibc 2.38+ — Ubuntu 24.04+, Fedora 38+, SteamOS 3.7+ +- EA account (free) at ea.com + +**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 diff --git a/scripts/setup-kyber-linux.sh b/scripts/setup-kyber-linux.sh index a5b7418..ce2ec54 100755 --- a/scripts/setup-kyber-linux.sh +++ b/scripts/setup-kyber-linux.sh @@ -1,574 +1,173 @@ #!/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 "" "" 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 with a long EA auth URL. This blocks 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: Replace Proton's own cmd.exe (files/lib/wine/x86_64-windows/cmd.exe) -# with a tiny shim that captures any http URL from its command line, writes -# it to C:\kyber_oauth_url.txt, and exits 0. A Linux-side watcher calls -# xdg-open on that file to open the browser. Wine loads cmd.exe from Proton's -# installation, not the prefix — prefix replacements, IFEO, and -# WINEDLLOVERRIDES all have no effect, so Proton's copy must be replaced. +# 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 # -# After a Proton Experimental update, cmd.exe is restored; re-run this script. +# ── Login flow ───────────────────────────────────────────────────────────── # -# ── Why NOT to run Kyber inside Wolf / Games-on-Whales ───────────────────── +# 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. # -# 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. +# ── Hosting a private server ─────────────────────────────────────────────── # -# ── WebView2 ─────────────────────────────────────────────────────────────── +# 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. # -# 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. +# Bots: SWBF2 fills empty player slots with AI automatically. No separate +# bot count setting is needed — just start the server and join it. # -# ── Headless note ────────────────────────────────────────────────────────── +# ── Why NOT Wine/Proton for the Kyber launcher ───────────────────────────── # -# 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. +# The Windows Kyber launcher (kyber_launcher.exe) has a fatal flaw on Linux: +# its EA OAuth flow calls cmd /c start "" "" 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 with Proton (GE-Proton +# recommended). Run setup-swbf2-linux.sh first if needed. +# 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. # # ── 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" - else - 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 - fi fi -echo "Kyber installer: $KYBER_INSTALLER" -# ── 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." - echo "" -fi - -# ── Build Kyber Wine prefix and run the installer ────────────────────────── +VERSION=$(echo "$APPIMAGE_URL" | grep -oP 'v[\d.a-z-]+' | head -1) +echo " Latest: $VERSION" +echo " URL: $APPIMAGE_URL" 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" +# ── Download ─────────────────────────────────────────────────────────────── +mkdir -p "$INSTALL_DIR" +APPIMAGE_PATH="$INSTALL_DIR/KyberLinuxPort.AppImage" -# 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" +CURRENT_VERSION="" +if [ -f "$APPIMAGE_PATH.version" ]; then + CURRENT_VERSION=$(cat "$APPIMAGE_PATH.version") 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." +if [ -f "$APPIMAGE_PATH" ] && [ "$CURRENT_VERSION" = "$VERSION" ]; then + echo "[1/3] Already up to date ($VERSION) — skipping download." 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 + 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 - -# 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..." +# ── Desktop entry + bin symlink ──────────────────────────────────────────── +echo "[3/3] Installing desktop entry and launcher..." - # 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 +mkdir -p "$(dirname "$DESKTOP_FILE")" "$HOME/.local/bin" - 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 +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 -# ── Install cmd shim ────────────────────────────────────────────────────── -# Wine's built-in cmd.exe crashes (exit 0xC0000005) when Kyber calls: -# cmd /c start "" "" -# -# Root cause: Wine loads cmd.exe from Proton's OWN installation directory -# files/lib/wine/x86_64-windows/cmd.exe -# NOT from the prefix system32. Replacing the prefix copy, using IFEO registry -# keys, or WINEDLLOVERRIDES all have no effect — Proton's copy always wins. -# -# Fix: replace Proton's cmd.exe with a tiny shim that scans its command line -# for an http URL, writes it to C:\kyber_oauth_url.txt, then exits 0. A -# Linux-side watcher calls xdg-open on that URL to open the system browser. -# The original is backed up as cmd.exe.bak next to it. -# -# After a Proton update the shim will be overwritten; re-run this script. -echo "" -echo "[3/7] Installing cmd shim for OAuth login..." - -SHIM_DIR="$KYBER_PFX/pfx/drive_c/shim" -SHIM_EXE_PATH="$SHIM_DIR/kyber_cmd.exe" -PROTON_CMD="$PROTON_DIR/files/lib/wine/x86_64-windows/cmd.exe" -PROTON_CMD_BAK="${PROTON_CMD}.bak" - -# Detect if Proton's cmd.exe is already our shim (small) or the original (large) -PROTON_CMD_SIZE=$(stat -c%s "$PROTON_CMD" 2>/dev/null || echo 0) - -compile_shim() { - 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 — login may not work." - echo " sudo apt install mingw-w64 && $0" - return 1 - } - fi - local SHIM_C="/tmp/kyber_cmd_shim_$$.c" - local SHIM_OUT="/tmp/kyber_cmd_shim_$$.exe" - cat > "$SHIM_C" << 'CEOF' -#include -static void write_url(const char *p, int len) { - HANDLE h = CreateFileA("C:\\kyber_oauth_url.txt", - GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (h == INVALID_HANDLE_VALUE) return; - DWORD w; WriteFile(h, p, len, &w, NULL); - CloseHandle(h); -} -int WINAPI mainCRTStartup(void) { - const char *cl = GetCommandLineA(); - while (*cl) { - if ((cl[0]=='h'||cl[0]=='H') && cl[1]=='t' && cl[2]=='t' && cl[3]=='p') { - const char *end = cl; - while (*end && *end != ' ' && *end != '"') end++; - write_url(cl, (int)(end - cl)); - ExitProcess(0); - } - cl++; - } - ExitProcess(0); -} -CEOF - mkdir -p "$SHIM_DIR" - if x86_64-w64-mingw32-gcc -O2 -ffreestanding -nostdlib \ - -mno-stack-arg-probe \ - -e mainCRTStartup -o "$SHIM_OUT" "$SHIM_C" \ - -lkernel32; then - cp "$SHIM_OUT" "$SHIM_EXE_PATH" - echo " Shim compiled: $(stat -c%s "$SHIM_EXE_PATH") bytes" - else - echo " WARNING: cmd shim compile failed — login may not work." - rm -f "$SHIM_C" "$SHIM_OUT" - return 1 - fi - rm -f "$SHIM_C" "$SHIM_OUT" -} - -# Compile shim if missing or obviously wrong size (>100KB = real cmd.exe) -SHIM_SIZE=$(stat -c%s "$SHIM_EXE_PATH" 2>/dev/null || echo 0) -if [ "$SHIM_SIZE" -gt 100000 ]; then - echo " Stale shim detected ($SHIM_SIZE bytes). Recompiling..." - rm -f "$SHIM_EXE_PATH" - SHIM_SIZE=0 -fi -if [ "$SHIM_SIZE" -eq 0 ]; then - compile_shim || true -fi - -# Replace Proton's cmd.exe if it is not already our shim -if [ -f "$SHIM_EXE_PATH" ]; then - if [ "$PROTON_CMD_SIZE" -gt 100000 ]; then - # Original: back it up and replace - cp "$PROTON_CMD" "$PROTON_CMD_BAK" 2>/dev/null || true - chmod u+w "$PROTON_CMD" - cp "$SHIM_EXE_PATH" "$PROTON_CMD" - chmod u-w "$PROTON_CMD" - echo " Proton cmd.exe replaced with shim." - echo " (Backup: $PROTON_CMD_BAK)" - echo " NOTE: Re-run this script after a Proton Experimental update." - else - echo " Proton cmd.exe already replaced ($(stat -c%s "$PROTON_CMD") bytes)." - fi -else - echo " WARNING: shim not compiled. Install mingw-w64 and re-run:" - echo " sudo apt install mingw-w64 && $0" -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('/dev/null || true echo "" -echo "[7/7] Done." +echo "=== Kyber Setup Complete ===" echo "" -echo "=== Kyber Setup Complete (native Linux) ===" +echo "Launch Kyber:" +echo " From terminal: kyber" +echo " From app menu: search 'Kyber Launcher'" +echo " Direct: $APPIMAGE_PATH" 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:/?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 "First run:" +echo " 1. Click 'EA Account' and log in with your EA credentials." +echo " 2. Click 'Skip' on the Nexus Mods step (optional — only needed for mods)." +echo " 3. You're in. Browse servers on HOME or create one under HOST." 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 "Hosting a private server:" +echo " HOST → pick maps → set a name and PASSWORD → Start Server." +echo " Share the server name + password with friends." +echo " Bots fill empty slots automatically — no separate setting needed." 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 "SWBF2 must be installed via Steam (AppID 1237950) with GE-Proton." +echo "GE-Proton: https://github.com/GloriousEggroll/proton-ge-custom/releases" 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 "" -echo " After a Proton Experimental update:" -echo " Re-run $0 to replace the restored cmd.exe with the shim again." +echo "To update Kyber later, re-run this script." From 01796c3041c346869f238e120adbd78cb2e5412f Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 14:20:16 +0000 Subject: [PATCH 4/6] docs: clarify that SWBF2 must be launched from Steam before using Kyber MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kyber does not launch the game itself — Steam must start SWBF2 first and let it reach the main menu. Added this to both the script output and the README gaming section. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs --- README.md | 16 ++++++++++++---- scripts/setup-kyber-linux.sh | 15 ++++++++++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 0bd98d0..200fffe 100644 --- a/README.md +++ b/README.md @@ -165,15 +165,23 @@ chmod +x scripts/setup-kyber-linux.sh The script downloads the latest AppImage, installs a desktop entry, and creates a `kyber` command in `~/.local/bin`. -**First run:** +**Every time you want to play:** +1. **Launch SWBF2 from Steam first** — let it fully load to the main menu +2. Launch Kyber (`kyber` or from the app menu) +3. In Kyber: join a server (HOME) or create one (HOST) +4. Kyber connects to the already-running game instance + +Kyber does not launch the game itself — Steam must start it first. + +**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. Browse servers on **HOME** or create one under **HOST** +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 name + password with friends; they search by name in HOME -- SWBF2 fills empty slots with AI automatically — no separate bot setting +- Share the server name + password with friends; they search by name in HOME +- SWBF2 fills empty slots with AI automatically — no separate bot setting needed **Requirements:** - SWBF2 (Steam AppID 1237950) installed with GE-Proton (recommended over diff --git a/scripts/setup-kyber-linux.sh b/scripts/setup-kyber-linux.sh index ce2ec54..dfcbf35 100755 --- a/scripts/setup-kyber-linux.sh +++ b/scripts/setup-kyber-linux.sh @@ -157,15 +157,20 @@ echo " From terminal: kyber" echo " From app menu: search 'Kyber Launcher'" echo " Direct: $APPIMAGE_PATH" echo "" -echo "First run:" +echo "Every time you want to play:" +echo " 1. Launch SWBF2 from Steam first — let it fully load to the main menu." +echo " 2. Then launch Kyber and join or host a server." +echo " Kyber does not launch the game itself — Steam must start SWBF2 first." +echo "" +echo "First run (one-time):" echo " 1. Click 'EA Account' and log in with your EA credentials." echo " 2. Click 'Skip' on the Nexus Mods step (optional — only needed for mods)." -echo " 3. You're in. Browse servers on HOME or create one under HOST." +echo " 3. EA login is cached — you stay logged in across sessions." echo "" -echo "Hosting a private server:" +echo "Hosting a private server with bots:" echo " HOST → pick maps → set a name and PASSWORD → Start Server." -echo " Share the server name + password with friends." -echo " Bots fill empty slots automatically — no separate setting needed." +echo " Share the server name + password with friends (they search by name in HOME)." +echo " Bots fill empty slots automatically — no separate bot setting needed." echo "" echo "SWBF2 must be installed via Steam (AppID 1237950) with GE-Proton." echo "GE-Proton: https://github.com/GloriousEggroll/proton-ge-custom/releases" From d3dfc900b83012ceedc985d0ac692aa7bf196be0 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 14:35:50 +0000 Subject: [PATCH 5/6] docs: add Game Not Found troubleshooting for Kyber When Kyber can't locate SWBF2 it shows a SET GAME FOLDER dialog. Document the find command to locate the install path on any system. Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs --- README.md | 8 ++++++++ scripts/setup-kyber-linux.sh | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/README.md b/README.md index 200fffe..e2a3783 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,14 @@ creates a `kyber` command in `~/.local/bin`. Kyber does not launch the game itself — Steam must start it first. +**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) diff --git a/scripts/setup-kyber-linux.sh b/scripts/setup-kyber-linux.sh index dfcbf35..714a611 100755 --- a/scripts/setup-kyber-linux.sh +++ b/scripts/setup-kyber-linux.sh @@ -172,6 +172,10 @@ echo " HOST → pick maps → set a name and PASSWORD → Start Server." echo " Share the server name + password with friends (they search by name in HOME)." echo " Bots fill empty slots automatically — no separate bot setting needed." 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 "" echo "SWBF2 must be installed via Steam (AppID 1237950) with GE-Proton." echo "GE-Proton: https://github.com/GloriousEggroll/proton-ge-custom/releases" echo "" From b37c50dbccededcbdd171e56a9acb9d7cffd7f66 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 25 Jun 2026 15:10:44 +0000 Subject: [PATCH 6/6] kyber: correct bot settings, launch order, bwrap fix, window focus tip - Bots are set via BOTS TEAM 1 / BOTS TEAM 2 sliders in HOST panel (not automatic) - Correct launch order: Steam open but don't click Play; Kyber launches SWBF2 itself - Document Alt+Tab / taskbar click workaround for window focus issue after game loads - Add bwrap unprivileged userns fix (Ubuntu 24.04 blocks by default); script applies automatically with sudo and persists via /etc/sysctl.d/99-userns.conf - Script now checks/fixes userns at step 0 before download - Kyber manages its own GE-Proton; no need to configure Proton for SWBF2 in Steam Co-Authored-By: Claude Sonnet 4.6 Claude-Session: https://claude.ai/code/session_01D8ckUJQtj1pH8jtAddBDZs --- README.md | 28 ++++++-- scripts/setup-kyber-linux.sh | 124 +++++++++++++++++++++++++++++++---- 2 files changed, 131 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index e2a3783..43fc8a2 100644 --- a/README.md +++ b/README.md @@ -166,12 +166,15 @@ 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. **Launch SWBF2 from Steam first** — let it fully load to the main menu -2. Launch Kyber (`kyber` or from the app menu) +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 connects to the already-running game instance +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 -Kyber does not launch the game itself — Steam must start it first. +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. @@ -189,13 +192,24 @@ Paste that path into the SET GAME FOLDER dialog. **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 -- SWBF2 fills empty slots with AI automatically — no separate bot setting needed +- 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 with GE-Proton (recommended over - Proton Experimental for game stability) +- 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 diff --git a/scripts/setup-kyber-linux.sh b/scripts/setup-kyber-linux.sh index 714a611..7759b47 100755 --- a/scripts/setup-kyber-linux.sh +++ b/scripts/setup-kyber-linux.sh @@ -18,6 +18,7 @@ # # 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) # # ── Login flow ───────────────────────────────────────────────────────────── # @@ -27,6 +28,20 @@ # 4. Kyber completes authentication via Maxima (no redirect hacks needed). # 5. Click "Skip" on Nexus Mods if you don't use mods. # +# ── How to play ──────────────────────────────────────────────────────────── +# +# Kyber launches SWBF2 itself — do NOT launch SWBF2 from Steam first. +# If Steam's SWBF2 is running when Kyber starts, kill it. +# +# 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. +# # ── Hosting a private server ─────────────────────────────────────────────── # # 1. In Kyber, click HOST. @@ -52,11 +67,37 @@ # bypasses all of this entirely. # # ── Prerequisites ────────────────────────────────────────────────────────── -# a. SWBF2 (AppID 1237950) installed via Steam with Proton (GE-Proton -# recommended). Run setup-swbf2-linux.sh first if needed. +# 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 @@ -83,6 +124,48 @@ if [ "$GLIBC_MAJOR" -lt 2 ] || { [ "$GLIBC_MAJOR" -eq 2 ] && [ "$GLIBC_MINOR" -l echo "" 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 " 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 "" + # ── Fetch latest release URL ─────────────────────────────────────────────── echo "[1/3] Fetching latest Kyber Linux release..." API_URL="https://api.github.com/repos/${KYBER_REPO}/releases/latest" @@ -120,7 +203,7 @@ if [ -f "$APPIMAGE_PATH.version" ]; then fi if [ -f "$APPIMAGE_PATH" ] && [ "$CURRENT_VERSION" = "$VERSION" ]; then - echo "[1/3] Already up to date ($VERSION) — skipping download." + 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" @@ -158,25 +241,38 @@ echo " From app menu: search 'Kyber Launcher'" echo " Direct: $APPIMAGE_PATH" echo "" echo "Every time you want to play:" -echo " 1. Launch SWBF2 from Steam first — let it fully load to the main menu." -echo " 2. Then launch Kyber and join or host a server." -echo " Kyber does not launch the game itself — Steam must start SWBF2 first." +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 " 2. Click 'Skip' on the Nexus Mods step (optional — only needed for mods)." +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 "Hosting a private server with bots:" -echo " HOST → pick maps → set a name and PASSWORD → Start Server." -echo " Share the server name + password with friends (they search by name in HOME)." -echo " Bots fill empty slots automatically — no separate bot setting needed." -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 "SWBF2 must be installed via Steam (AppID 1237950) with GE-Proton." -echo "GE-Proton: https://github.com/GloriousEggroll/proton-ge-custom/releases" +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 +echo "SWBF2 must be installed via Steam (AppID 1237950)." echo "To update Kyber later, re-run this script."