#!/bin/bash # 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 in 2022. Kyber went open-source # under GPL in January 2026. # # ── The RIGHT way: native Linux AppImage ─────────────────────────────────── # # As of 2026 there is an unofficial native Linux port of Kyber: # https://github.com/simonlinuxcraft/kyber-linuxport-unofficial # # 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). # # 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 ───────────────────────────────────────────────────────────── # # 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. # # ── 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. # 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 "" "" 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. 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 # # ── "Origin Error: title installed in language not entitled" ─────────────── # # If Kyber launches SWBF2 but the game shows an Origin Error about language # entitlement, Maxima's Wine prefix is missing the Origin locale registry keys. # Maxima writes these via umu-run reg commands on first run, but they can fail # silently (exit code 1) leaving the keys absent. # # Fix: manually import the registry keys into Maxima's Wine prefix. # Create a file (e.g. /tmp/swbf2_fix.reg) with: # # Windows Registry Editor Version 5.00 # [HKEY_LOCAL_MACHINE\Software\Origin Games\1035052] # "locale"="en_US" # "displayname"="STAR WARS Battlefront II" # [HKEY_LOCAL_MACHINE\Software\Wow6432Node\Origin Games\1035052] # "locale"="en_US" # "displayname"="STAR WARS Battlefront II" # [HKEY_LOCAL_MACHINE\Software\Electronic Arts\EA Desktop] # "InstallSuccessful"="true" # [HKEY_LOCAL_MACHINE\Software\Origin] # "InstallSuccessful"="true" # "ClientPath"="C:\\Windows\\System32\\conhost.exe" # [HKEY_CURRENT_USER\Control Panel\International] # "Locale"="00000409" # "LocaleName"="en-US" # "sLanguage"="ENU" # # Then import it: # WINEPREFIX=~/.local/share/maxima/wine/prefix wine64 regedit /tmp/swbf2_fix.reg # # ── "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 set -euo pipefail 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 Setup ===" echo "" # ── 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 "" 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" 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 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 echo "SWBF2 must be installed via Steam (AppID 1237950)." echo "To update Kyber later, re-run this script."