#!/usr/bin/env bash set -u PLUGIN_ID="local.omarchy-citizen" PLUGIN_VERSION="0.8.0" CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/starcitizen-lug" DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}" CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/omarchy-citizen" STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/omarchy-citizen" UPDATE_CACHE="$CACHE_DIR/lug-update-status" LOG_FILE="$STATE_DIR/omarchy-citizen.log" WINE_CONF="$CONFIG_DIR/winedir.conf" GAME_CONF="$CONFIG_DIR/gamedir.conf" SELF="$(readlink -f "${BASH_SOURCE[0]}")" PLUGIN_DIR="$(cd -- "$(dirname -- "$SELF")" && pwd)" SANITIZER="$PLUGIN_DIR/support-sanitize.py" BACKEND_EXTERNAL="$HOME/.local/lib/omarchy-citizen/omarchy-citizen-backend" BACKEND_BUNDLED="$PLUGIN_DIR/backend/bin/omarchy-citizen-backend" MANAGED_LUG="$DATA_DIR/omarchy-citizen/vendor/lug-helper/lug-helper.appimage" MANAGED_WINE="$DATA_DIR/omarchy-citizen/vendor/wine/current" GUI_PACKAGES=(zenity polkit cabextract unzip) mkdir -p "$CACHE_DIR" "$STATE_DIR" rotate_log() { local max_bytes=$((4 * 1024 * 1024)) local keep_bytes=$((2 * 1024 * 1024)) if [[ -f "$LOG_FILE" ]] && (( $(stat -c '%s' "$LOG_FILE" 2>/dev/null || echo 0) > max_bytes )); then tail -c "$keep_bytes" "$LOG_FILE" > "$LOG_FILE.tmp" 2>/dev/null || true mv -f "$LOG_FILE.tmp" "$LOG_FILE" 2>/dev/null || true fi } rotate_log ACTION="${1:-primary}" log_line() { local message="$*" printf '%s [%s] %s\n' "$(date '+%Y-%m-%dT%H:%M:%S%z')" "$$" "$message" >> "$LOG_FILE" 2>/dev/null || true } log_line "action=$ACTION version=$PLUGIN_VERSION" find_backend() { if [[ -x "$BACKEND_EXTERNAL" ]]; then printf '%s\n' "$BACKEND_EXTERNAL" return 0 fi if [[ -x "$BACKEND_BUNDLED" ]]; then printf '%s\n' "$BACKEND_BUNDLED" return 0 fi return 1 } backend_status() { local game_output="" plugin_output="" health="setup" prefix_state="missing" launcher_state="missing" game_state="missing" local wine_version="" dxvk_version="" lug_version="" hardware="unknown" hardware_reason="" gpu="" vulkan="unknown" if game_output="$(backend_game_status 2>/dev/null)"; then health="$(printf '%s\n' "$game_output" | sed -n 's/^health=//p' | head -n1)" prefix_state="$(printf '%s\n' "$game_output" | sed -n 's/^prefix_state=//p' | head -n1)" launcher_state="$(printf '%s\n' "$game_output" | sed -n 's/^launcher_state=//p' | head -n1)" game_state="$(printf '%s\n' "$game_output" | sed -n 's/^game_state=//p' | head -n1)" wine_version="$(printf '%s\n' "$game_output" | sed -n 's/^wine_version=//p' | head -n1)" dxvk_version="$(printf '%s\n' "$game_output" | sed -n 's/^dxvk_version=//p' | head -n1)" lug_version="$(printf '%s\n' "$game_output" | sed -n 's/^lug_version=//p' | head -n1)" hardware="$(printf '%s\n' "$game_output" | sed -n 's/^hardware=//p' | head -n1)" hardware_reason="$(printf '%s\n' "$game_output" | sed -n 's/^hardware_reason=//p' | head -n1)" gpu="$(printf '%s\n' "$game_output" | sed -n 's/^gpu=//p' | head -n1)" vulkan="$(printf '%s\n' "$game_output" | sed -n 's/^vulkan=//p' | head -n1)" fi printf 'plugin_version=%s\n' "$PLUGIN_VERSION" printf 'health=%s\n' "${health:-setup}" printf 'deps=ready\n' printf 'deps_missing=\n' printf 'helper=%s\n' "$([[ -n "$lug_version" ]] && echo ready || echo missing)" printf 'helper_managed=autopilot\n' printf 'helper_version=%s\n' "$lug_version" printf 'update=unknown\n' printf 'update_version=\n' printf 'prefix=%s\n' "${prefix_state:-missing}" if [[ "$prefix_state" == "partial" ]]; then printf 'prefix_detail=unvollständig\n'; else printf 'prefix_detail=\n'; fi printf 'launcher=%s\n' "${launcher_state:-missing}" printf 'game=%s\n' "${game_state:-missing}" printf 'hardware=%s\n' "$hardware" printf 'hardware_reason=%s\n' "$hardware_reason" printf 'hardware_gpu=%s\n' "$gpu" printf 'hardware_vulkan=%s\n' "$vulkan" if plugin_output="$(backend_status 2>/dev/null)"; then printf 'backend=ready\n' else printf 'backend=missing\n' fi printf 'backend_version=%s\n' "$(printf '%s\n' "$plugin_output" | sed -n 's/^backend_version=//p' | head -n1)" printf 'plugin_managed=%s\n' "$(printf '%s\n' "$plugin_output" | sed -n 's/^managed=//p' | head -n1)" printf 'plugin_auto=%s\n' "$(printf '%s\n' "$plugin_output" | sed -n 's/^auto_apply=//p' | head -n1)" printf 'plugin_update=%s\n' "$(printf '%s\n' "$plugin_output" | sed -n 's/^update=//p' | head -n1)" printf 'plugin_local_commit=%s\n' "$(printf '%s\n' "$plugin_output" | sed -n 's/^local_commit=//p' | head -n1)" printf 'plugin_remote_commit=%s\n' "$(printf '%s\n' "$plugin_output" | sed -n 's/^remote_commit=//p' | head -n1)" printf 'autopilot=%s\n' "$(printf '%s\n' "$plugin_output" | sed -n 's/^auto_maintain=//p' | head -n1)" printf 'managed_lug_version=%s\n' "${lug_version}" printf 'managed_wine_version=%s\n' "${wine_version}" printf 'managed_dxvk_version=%s\n' "${dxvk_version}" printf 'maintenance_result=%s\n' "$(printf '%s\n' "$plugin_output" | sed -n 's/^maintenance_result=//p' | head -n1)" } hardware_info() { local out reason gpu vulkan out="$(backend_game_status 2>/dev/null || true)" reason="$(printf '%s\n' "$out" | sed -n 's/^hardware_reason=//p')" gpu="$(printf '%s\n' "$out" | sed -n 's/^gpu=//p')" vulkan="$(printf '%s\n' "$out" | sed -n 's/^vulkan=//p')" if command -v zenity >/dev/null 2>&1; then zenity --warning --width=600 --title="Omarchy Citizen – Hardware" --text="Star Citizen ist auf diesem System noch nicht spielbereit.\n\nGPU:\n${gpu:-nicht erkannt}\n\nVulkan: ${vulkan:-unbekannt}\n\n${reason:-Hardware-Prüfung fehlgeschlagen.}" >/dev/null 2>&1 || true else printf '%s\n' "$reason" fi } probe_detached_start() { local label="$1" shift "$@" >>"$LOG_FILE" 2>&1 & local pid=$! sleep 0.45 if ! kill -0 "$pid" 2>/dev/null; then local rc=0 wait "$pid" || rc=$? if (( rc != 0 )); then log_line "$label failed rc=$rc" return "$rc" fi else disown "$pid" 2>/dev/null || true fi log_line "$label accepted" return 0 } launch_terminal_action() { local action="$1" log_line "launch-terminal action=$action" # Prefer direct argv-safe terminal launching. This avoids the presentation # wrapper joining arguments into one shell command string. if command -v uwsm-app >/dev/null 2>&1 && command -v xdg-terminal-exec >/dev/null 2>&1; then if probe_detached_start "uwsm-xdg-terminal" \ setsid uwsm-app -- xdg-terminal-exec \ --app-id=org.omarchy.terminal \ --title="Omarchy Citizen Setup" \ -e bash "$SELF" "$action"; then printf 'terminal=opened\n' return 0 fi fi if command -v xdg-terminal-exec >/dev/null 2>&1; then if probe_detached_start "xdg-terminal" \ xdg-terminal-exec \ --title="Omarchy Citizen Setup" \ -e bash "$SELF" "$action"; then printf 'terminal=opened\n' return 0 fi fi # Final compatibility fallback for older/current Omarchy installations. if command -v omarchy-launch-floating-terminal-with-presentation >/dev/null 2>&1; then if probe_detached_start "omarchy-floating-terminal" \ omarchy-launch-floating-terminal-with-presentation bash "$SELF" "$action"; then printf 'terminal=opened\n' return 0 fi fi echo "Das Setup-Terminal konnte nicht geöffnet werden." >&2 notify_error "Das Setup-Terminal konnte nicht geöffnet werden. Bitte klicke auf „Support-Paket erstellen“. Der fehlgeschlagene Startversuch ist dort protokolliert." return 1 } backup_lug_target_configs() { local stamp="$1" local f mkdir -p "$CONFIG_DIR" for f in "$WINE_CONF" "$GAME_CONF"; do if [[ -f "$f" ]]; then mv -- "$f" "${f}.incomplete-${stamp}" log_line "backed up LUG config $(basename "$f") -> $(basename "${f}.incomplete-${stamp}")" fi done } stop_prefix_wineserver() { local prefix="$1" local wineserver="" # Kill only a wineserver associated with the runner inside this target prefix. # Do not kill unrelated Wine applications globally. wineserver="$(find "$prefix/runners" -type f -path '*/bin/wineserver' -perm -u+x -print -quit 2>/dev/null || true)" if [[ -n "$wineserver" ]]; then WINEPREFIX="$prefix" "$wineserver" -k >>"$LOG_FILE" 2>&1 || true sleep 0.5 fi } recover_incomplete_prefix_terminal() { set -Ee exec > >(tee -a "$LOG_FILE") 2>&1 local prefix stamp backup datap4k="" recovery_failed() { local rc=$? trap - ERR echo echo "==================================================" echo " REPARATUR KONNTE NICHT ABGESCHLOSSEN WERDEN" echo "==================================================" echo echo "Fehlercode: $rc" echo echo "Dein vorheriger Ordner wurde nicht gelöscht." echo "Im SC-Panel kannst du jetzt „Support-Paket erstellen“ wählen." echo log_line "recover-prefix-terminal failed rc=$rc" notify "Omarchy Citizen" "Setup-Reparatur fehlgeschlagen. Bitte erstelle ein Support-Paket." if [[ -t 0 ]]; then read -r -p "Drücke Enter, um dieses Fenster zu schließen … " _ || true else sleep 10 fi exit "$rc" } trap recovery_failed ERR prefix="$(find_incomplete_prefix 2>/dev/null)" || { echo "Es wurde kein unvollständiger Wine-Prefix mehr gefunden." echo "Starte stattdessen die normale Einrichtung." log_line "recover-prefix-terminal no incomplete prefix found" trap - ERR return 0 } datap4k="$prefix/drive_c/Program Files/Roberts Space Industries/StarCitizen/LIVE/Data.p4k" echo echo "==================================================" echo " Omarchy Citizen – Setup sicher reparieren" echo "==================================================" echo echo "Unvollständiger Wine-Prefix:" echo " $prefix" echo # Never move real game data ourselves. if [[ -f "$datap4k" ]]; then echo "Vorhandene Star-Citizen-Spieldaten wurden erkannt." echo "Omarchy Citizen verschiebt diese Daten nicht selbst." echo "Die Reparatur wird an den LUG Helper übergeben." echo log_line "recover-prefix-terminal Data.p4k present; delegate to LUG" run_helper_visible --install trap - ERR return 0 fi stamp="$(date '+%Y%m%d-%H%M%S')" backup="${prefix}-incomplete-${stamp}" echo "Der halbfertige Ordner wird NICHT gelöscht." echo "Er wird gesichert als:" echo " $backup" echo echo "Danach wird der offizielle LUG-Installer sauber neu gestartet." echo log_line "recover-prefix-terminal prefix=$prefix backup=$backup" stop_prefix_wineserver "$prefix" mv -- "$prefix" "$backup" backup_lug_target_configs "$stamp" echo "✓ Backup erstellt." echo echo "Starte frische LUG-Einrichtung …" echo run_helper_visible --install echo echo "Prüfe den neu angelegten Wine-Prefix …" if read_prefix >/dev/null 2>&1; then echo "✓ Wine-Prefix ist vollständig initialisiert." log_line "recover-prefix-terminal succeeded" trap - ERR notify "Omarchy Citizen" "Wine-Prefix wurde erfolgreich neu erstellt." echo echo "Reparatur abgeschlossen. Du kannst dieses Fenster schließen." return 0 fi echo echo "Der LUG-Installer wurde beendet, aber drive_c/system.reg/user.reg" echo "sind weiterhin nicht vollständig vorhanden." echo log_line "recover-prefix-terminal LUG returned but prefix still incomplete" return 1 } recover_incomplete_prefix() { launch_terminal_action recover-prefix-terminal } guided_setup_interactive() { log_line "guided-setup begin" if ! find_helper >/dev/null 2>&1; then launch_terminal_action install-stack-terminal return 0 fi if find_incomplete_prefix >/dev/null 2>&1; then log_line "guided setup found incomplete prefix; opening visible recovery terminal" launch_terminal_action recover-prefix-terminal return $? fi run_helper_sync --preflight-check || true if ! read_prefix >/dev/null 2>&1; then run_helper_sync --install || true elif ! prefix="$(read_prefix 2>/dev/null)" || [[ ! -f "$prefix/sc-launch.sh" ]]; then run_helper_sync --update-launch-script || true fi prefix="$(read_prefix 2>/dev/null || true)" if [[ -n "$prefix" && -f "$prefix/sc-launch.sh" ]]; then game_dir="$(read_game_dir 2>/dev/null || true)" if [[ -z "$game_dir" || ! -f "$game_dir/LIVE/Bin64/StarCitizen.exe" ]]; then log_line "guided-setup opening RSI Launcher for game installation" nohup bash "$prefix/sc-launch.sh" >>"$LOG_FILE" 2>&1 & fi fi notify "Omarchy Citizen" "Einrichtung abgeschlossen. Falls Star Citizen noch fehlt, ist der RSI Launcher für die Installation geöffnet." log_line "guided-setup end" } guided_setup() { launch_terminal_action install-stack-terminal } install_stack_terminal() { set -Ee exec > >(tee -a "$LOG_FILE") 2>&1 setup_failed() { local rc=$? trap - ERR echo echo "==================================================" echo " SETUP KONNTE NICHT ABGESCHLOSSEN WERDEN" echo "==================================================" echo echo "Fehlercode: $rc" echo "Im SC-Panel kannst du mit einem Klick ein Support-Paket erstellen." log_line "owned-stack setup failed rc=$rc" notify "Omarchy Citizen" "Setup fehlgeschlagen. Bitte Support-Paket erstellen." if [[ -t 0 ]]; then read -r -p "Drücke Enter zum Schließen … " _ || true; fi exit "$rc" } trap setup_failed ERR echo echo "==================================================" echo " Omarchy Citizen – Spielbereit machen" echo "==================================================" echo echo "Omarchy Citizen verwaltet den kritischen Gaming-Stack jetzt selbst." echo "Der LUG Helper entscheidet nicht mehr, welche Wine-Version installiert wird." echo local hw hw="$(backend_game_status)" if [[ "$(printf '%s\n' "$hw" | sed -n 's/^health=//p')" == "hardware-blocked" ]]; then echo "Hardware-Prüfung nicht bestanden:" printf '%s\n' "$hw" | grep -E '^(hardware_reason|gpu|vulkan)=' trap - ERR return 2 fi echo "1/4 Autopilot aktivieren und Quellen synchronisieren …" autopilot_enable echo echo "2/4 Kompatiblen Wine-Runner prüfen …" wine_doctor echo echo "3/4 Wine-Prefix, Komponenten und RSI Launcher installieren/reparieren …" backend_game_install echo echo "4/4 Gaming-Stack abschließend synchronisieren …" autopilot_maintain || true trap - ERR echo echo "==================================================" echo " SOFTWARE-STACK BEREIT" echo "==================================================" echo echo "Der RSI Launcher kann jetzt gestartet werden." echo "Anmeldung und der eigentliche Spieldownload erfolgen im offiziellen RSI Launcher." notify "Omarchy Citizen" "Software-Stack ist bereit. RSI Launcher kann gestartet werden." } update_helper_terminal() { set -Ee exec > >(tee -a "$LOG_FILE") 2>&1 echo "Synchronisiere LUG Helper, Wine Runner und DXVK über Autopilot …" autopilot_maintain echo "✓ Gaming-Stack ist aktuell." } system_update_terminal() { set -e log_line "omarchy update begin" omarchy update rm -f "$UPDATE_CACHE" log_line "omarchy update end rc=0" } launch_game() { log_line "launch via owned backend" if ! backend_game_launch >>"$LOG_FILE" 2>&1; then notify_error "Star Citizen konnte nicht gestartet werden. Nutze „Problem beheben“ oder erstelle ein Support-Paket." return 1 fi } primary_action() { local st health backend backend="$(find_backend 2>/dev/null || true)" [[ -n "$backend" ]] && systemctl --user start omarchy-citizen-update.service >/dev/null 2>&1 || true st="$(status)" health="$(printf '%s\n' "$st" | sed -n 's/^health=//p' | head -n1)" case "$health" in ready|install-game) launch_game ;; hardware-blocked) hardware_info ;; *) launch_terminal_action install-stack-terminal ;; esac } repair_stack_terminal() { set -Ee exec > >(tee -a "$LOG_FILE") 2>&1 echo "Omarchy Citizen prüft und repariert den gesamten Star-Citizen-Stack …" autopilot_maintain || true backend_game_repair echo "Reparatur abgeschlossen." } repair_assistant_interactive() { local st health st="$(status)" health="$(printf '%s\n' "$st" | sed -n 's/^health=//p' | head -n1)" if [[ "$health" == "hardware-blocked" ]]; then hardware_info; return; fi launch_terminal_action repair-stack-terminal } repair_assistant() { repair_assistant_interactive; } sanitize_to() { local source="$1" local target="$2" if [[ ! -f "$source" ]]; then return 0 fi if command -v python3 >/dev/null 2>&1 && [[ -f "$SANITIZER" ]]; then python3 "$SANITIZER" < "$source" > "$target" else sed \ -e "s#${HOME//\#/\\#}#~#g" \ -e 's/[A-Za-z0-9._%+-]\+@[A-Za-z0-9.-]\+\.[A-Za-z]\{2,\}//g' \ "$source" > "$target" fi } capture_sanitized() { local target="$1" shift local tmp="$CACHE_DIR/capture-$$-$RANDOM.txt" { "$@" } >"$tmp" 2>&1 || true sanitize_to "$tmp" "$target" rm -f "$tmp" } support_build() { local stamp support_id work downloads archive prefix="" game_dir="" stamp="$(date '+%Y%m%d-%H%M%S')" support_id="OC-${stamp}-$(printf '%s-%s-%s' "$stamp" "$$" "$RANDOM" | sha256sum | cut -c1-6)" work="$CACHE_DIR/support-$support_id" if command -v xdg-user-dir >/dev/null 2>&1; then downloads="$(xdg-user-dir DOWNLOAD 2>/dev/null || true)" else downloads="" fi [[ -n "$downloads" && -d "$downloads" ]] || downloads="$HOME/Downloads" mkdir -p "$downloads" "$work" log_line "support build id=$support_id" cat > "$work/README_SUPPORT.txt" < "$work/citizen-status.txt" if backend="$(find_backend 2>/dev/null)"; then capture_sanitized "$work/updater-status.json" "$backend" status --json fi if [[ -f "$STATE_DIR/updater.log" ]]; then tail -n 1200 "$STATE_DIR/updater.log" > "$CACHE_DIR/updater-log-$$.txt" sanitize_to "$CACHE_DIR/updater-log-$$.txt" "$work/updater.log" rm -f "$CACHE_DIR/updater-log-$$.txt" fi capture_sanitized "$work/omarchy-debug.txt" omarchy debug --no-sudo --print if backend="$(find_backend 2>/dev/null)"; then capture_sanitized "$work/autopilot-status.txt" "$backend" status capture_sanitized "$work/game-status.txt" "$backend" game-status capture_sanitized "$work/wine-doctor.txt" "$backend" doctor fi { echo "=== Kernel / Desktop ===" uname -rmo 2>/dev/null || true printf 'XDG_SESSION_TYPE=%s\n' "${XDG_SESSION_TYPE:-unknown}" printf 'XDG_CURRENT_DESKTOP=%s\n' "${XDG_CURRENT_DESKTOP:-unknown}" printf 'WAYLAND_DISPLAY=%s\n' "${WAYLAND_DISPLAY:-unknown}" echo echo "=== Memory ===" free -h 2>/dev/null || true echo echo "=== Filesystems ===" df -hT / "$HOME" 2>/dev/null || true echo echo "=== Block devices ===" lsblk -o NAME,SIZE,FSTYPE,FSAVAIL,MOUNTPOINTS 2>/dev/null || true echo echo "=== GPU ===" lspci -nnk 2>/dev/null | grep -A4 -Ei 'VGA|3D|Display' || true echo echo "=== Vulkan ===" if command -v vulkaninfo >/dev/null 2>&1; then vulkaninfo --summary 2>/dev/null || true else echo "vulkaninfo not installed" fi echo echo "=== Star Citizen kernel limits ===" sysctl vm.max_map_count 2>/dev/null || true printf 'hard_open_files=%s\n' "$(ulimit -Hn 2>/dev/null || true)" } > "$CACHE_DIR/system-$$.txt" sanitize_to "$CACHE_DIR/system-$$.txt" "$work/system.txt" rm -f "$CACHE_DIR/system-$$.txt" { echo "=== Relevant packages ===" pacman -Q omarchy omarchy-dev lug-helper zenity polkit cabextract unzip 2>/dev/null || true echo echo "=== AUR helper ===" yay --version 2>/dev/null | head -n 4 || true echo echo "=== Commands ===" for cmd in omarchy lug-helper yay zenity wine winetricks gamescope gamemoderun; do if command -v "$cmd" >/dev/null 2>&1; then printf '%s=present\n' "$cmd" else printf '%s=missing\n' "$cmd" fi done } > "$work/packages.txt" if command -v omarchy >/dev/null 2>&1; then capture_sanitized "$work/plugin-validation.txt" omarchy plugin validate "$PLUGIN_DIR" fi if [[ -f "$LOG_FILE" ]]; then tail -n 1500 "$LOG_FILE" > "$CACHE_DIR/plugin-log-$$.txt" sanitize_to "$CACHE_DIR/plugin-log-$$.txt" "$work/omarchy-citizen.log" rm -f "$CACHE_DIR/plugin-log-$$.txt" fi if [[ -f /tmp/omarchy-update.log ]]; then tail -n 500 /tmp/omarchy-update.log > "$CACHE_DIR/update-log-$$.txt" sanitize_to "$CACHE_DIR/update-log-$$.txt" "$work/omarchy-update-tail.log" rm -f "$CACHE_DIR/update-log-$$.txt" fi # LUG Helper writes the detailed Wine-prefix installation output here. # This is the most useful file when winetricks/Wine prefix creation fails. latest_lug_install_log="$(find /tmp -maxdepth 1 -type f -name 'lughelper-install-*.log' -printf '%T@ %p\n' 2>/dev/null \ | sort -nr | head -n1 | cut -d' ' -f2- || true)" if [[ -n "$latest_lug_install_log" && -f "$latest_lug_install_log" ]]; then tail -n 1200 "$latest_lug_install_log" > "$CACHE_DIR/lug-install-$$.txt" sanitize_to "$CACHE_DIR/lug-install-$$.txt" "$work/lug-helper-install-tail.log" rm -f "$CACHE_DIR/lug-install-$$.txt" fi configured="$(configured_prefix 2>/dev/null || true)" if [[ -n "$configured" ]]; then { printf 'configured_prefix=%s\n' "$configured" printf 'directory_exists=%s\n' "$([[ -d "$configured" ]] && echo yes || echo no)" printf 'drive_c=%s\n' "$([[ -d "$configured/drive_c" ]] && echo present || echo missing)" printf 'system_reg=%s\n' "$([[ -f "$configured/system.reg" ]] && echo present || echo missing)" printf 'user_reg=%s\n' "$([[ -f "$configured/user.reg" ]] && echo present || echo missing)" printf 'runner_dirs=%s\n' "$(find "$configured/runners" -mindepth 1 -maxdepth 1 -type d -printf '%f ' 2>/dev/null || true)" } > "$CACHE_DIR/prefix-health-$$.txt" sanitize_to "$CACHE_DIR/prefix-health-$$.txt" "$work/wine-prefix-health.txt" rm -f "$CACHE_DIR/prefix-health-$$.txt" fi prefix="$(read_prefix 2>/dev/null || true)" game_dir="$(read_game_dir 2>/dev/null || true)" { printf 'wine_prefix=%s\n' "${prefix:-not-configured}" printf 'game_dir=%s\n' "${game_dir:-not-configured}" printf 'launch_script=%s\n' "$([[ -n "$prefix" && -f "$prefix/sc-launch.sh" ]] && echo present || echo missing)" printf 'game_binary=%s\n' "$([[ -n "$game_dir" && -f "$game_dir/LIVE/Bin64/StarCitizen.exe" ]] && echo present || echo missing)" } > "$CACHE_DIR/paths-$$.txt" sanitize_to "$CACHE_DIR/paths-$$.txt" "$work/star-citizen-path-status.txt" rm -f "$CACHE_DIR/paths-$$.txt" if [[ -n "$prefix" && -f "$prefix/sc-launch.log" ]]; then tail -n 600 "$prefix/sc-launch.log" > "$CACHE_DIR/sc-launch-$$.txt" sanitize_to "$CACHE_DIR/sc-launch-$$.txt" "$work/sc-launch-tail.log" rm -f "$CACHE_DIR/sc-launch-$$.txt" fi if [[ -n "$prefix" && -f "$prefix/sc-launch.sh" ]]; then head -n 350 "$prefix/sc-launch.sh" > "$CACHE_DIR/sc-script-$$.txt" sanitize_to "$CACHE_DIR/sc-script-$$.txt" "$work/sc-launch-script-sanitized.txt" rm -f "$CACHE_DIR/sc-script-$$.txt" fi archive="$downloads/Omarchy-Citizen-Support-$support_id.tar.gz" tar -C "$work" -czf "$archive" . rm -rf "$work" log_line "support archive=$archive" if command -v wl-copy >/dev/null 2>&1; then printf '%s' "$archive" | wl-copy fi if command -v zenity >/dev/null 2>&1; then zenity --info --width=580 \ --title="Support-Paket erstellt" \ --text="Support-ID: $support_id Das bereinigte Support-Paket wurde in deinem Downloads-Ordner erstellt: $(basename "$archive") Der Dateipfad wurde – falls die Zwischenablage verfügbar ist – kopiert. Schicke dieses Archiv zusammen mit der Support-ID an den Plugin-Support." \ >/dev/null 2>&1 || true else notify "Omarchy Citizen" "Support-Paket erstellt: $(basename "$archive")" fi if command -v xdg-open >/dev/null 2>&1; then xdg-open "$downloads" >/dev/null 2>&1 & fi printf '%s\n' "$archive" } support_start() { support_build } open_debug_log() { if [[ ! -f "$LOG_FILE" ]]; then notify_error "Noch kein Debug-Log vorhanden." return 1 fi xdg-open "$LOG_FILE" >/dev/null 2>&1 & } open_log() { local prefix log prefix="$(read_prefix 2>/dev/null)" || { notify_error "Noch kein Star-Citizen-Wine-Prefix gefunden." return 1 } log="$prefix/sc-launch.log" if [[ ! -f "$log" ]]; then notify_error "Noch kein Start-Log gefunden. Starte den RSI Launcher mindestens einmal." return 1 fi xdg-open "$log" >/dev/null 2>&1 & } open_wiki() { xdg-open "https://wiki.starcitizen-lug.org" >/dev/null 2>&1 & } open_ngl_page() { xdg-open "https://www.nglproject.com/de#downloads" >/dev/null 2>&1 & } ngl_info() { local msg msg="NGL ist ein optionaler Community-Mod für den Windows-RSI-Launcher. Omarchy Citizen behandelt NGL als EXPERIMENTELL und installiert es nie ohne deine Auswahl und Bestätigung. Der Mod läuft im selben Wine-Prefix wie dein RSI Launcher." if command -v zenity >/dev/null 2>&1; then zenity --info --width=520 --title="Omarchy Citizen – NGL" --text="$msg" >/dev/null 2>&1 & else notify "Omarchy Citizen – NGL" "$msg" fi } ngl_install_interactive() { local prefix script selected workdir exe if ! command -v zenity >/dev/null 2>&1; then notify_error "Die NGL-Installation benötigt die grafische Dateiauswahl. Bitte zuerst das normale Setup ausführen." return 1 fi prefix="$(read_prefix 2>/dev/null)" || { notify_error "Star Citizen muss zuerst eingerichtet werden." return 1 } script="$prefix/sc-launch.sh" [[ -f "$script" ]] || { notify_error "Das Star-Citizen-Start-Script fehlt. Nutze zuerst „Problem beheben“." return 1 } selected="$(zenity \ --file-selection \ --title="NGL Installer auswählen" \ --filename="$HOME/Downloads/" \ --file-filter="NGL Installer | *.zip *.ZIP *.exe *.EXE" \ --file-filter="Alle Dateien | *" 2>/dev/null)" || return 0 [[ -f "$selected" ]] || return 1 workdir="$CACHE_DIR/ngl-installer" rm -rf -- "$workdir" mkdir -p "$workdir" case "${selected,,}" in *.zip) unzip -q "$selected" -d "$workdir" || { notify_error "Der NGL-Installer konnte nicht entpackt werden." return 1 } exe="$(find "$workdir" -type f \( -iname '*ngl*.exe' -o -iname '*installer*.exe' -o -iname '*setup*.exe' -o -iname '*.exe' \) \ -printf '%p\n' | head -n 1)" ;; *.exe) exe="$selected" ;; *) notify_error "Bitte eine NGL-ZIP- oder EXE-Datei auswählen." return 1 ;; esac [[ -n "${exe:-}" && -f "$exe" ]] || { notify_error "Kein Windows-Installer in der ausgewählten Datei gefunden." return 1 } if ! zenity --question --width=560 \ --title="NGL experimentell installieren?" \ --ok-label="Installer starten" \ --cancel-label="Abbrechen" \ --text="NGL ist Community-Code und läuft im selben Wine-Prefix wie dein RSI Launcher. Bitte nutze ausschließlich Downloads von nglproject.com. Fortfahren?" 2>/dev/null; then return 0 fi log_line "NGL installer confirmed by user" { printf 'wine ' printf '%q' "$exe" printf '\nexit\n' } | bash "$script" shell >>"$LOG_FILE" 2>&1 } ngl_install() { ngl_install_interactive } case "$ACTION" in status) status ;; hardware-info) hardware_info ;; autopilot-enable) autopilot_enable ;; autopilot-maintain) autopilot_maintain ;; wine-doctor) wine_doctor ;; primary) primary_action ;; setup) guided_setup ;; recover-prefix) recover_incomplete_prefix ;; recover-prefix-terminal) recover_incomplete_prefix_terminal ;; guided-setup-interactive) guided_setup_interactive ;; repair) repair_assistant ;; repair-interactive) repair_assistant_interactive ;; repair-stack-terminal) repair_stack_terminal ;; support) support_start ;; support-build) support_build ;; debug-log) open_debug_log ;; plugin-update-check) plugin_update_check ;; plugin-update-now) plugin_update_now ;; plugin-auto-enable) plugin_auto_enable ;; plugin-auto-disable) plugin_auto_disable ;; updater-log) open_updater_log ;; check-update) check_update ;; install-stack) launch_terminal_action install-stack-terminal ;; install-stack-terminal) install_stack_terminal ;; update-helper) launch_terminal_action update-helper-terminal ;; update-helper-terminal) update_helper_terminal ;; system-update) launch_terminal_action system-update-terminal ;; system-update-terminal) system_update_terminal ;; launch) launch_game ;; helper) run_helper_gui ;; install) run_helper_gui --install ;; preflight) run_helper_gui --preflight-check ;; runners) run_helper_gui --manage-runners ;; dxvk) run_helper_gui --manage-dxvk ;; repair-launch) run_helper_gui --update-launch-script ;; repair-rsi) run_helper_gui --update-rsi-launcher ;; dirs) run_helper_gui --show-directories ;; log) open_log ;; wiki) open_wiki ;; ngl-page) open_ngl_page ;; ngl-info) ngl_info ;; ngl-install) ngl_install ;; ngl-install-interactive) ngl_install_interactive ;; *) notify_error "Unbekannte Omarchy-Citizen-Aktion: $ACTION" exit 2 ;; esac