#!/usr/bin/env bash set -u if [[ $# -lt 2 ]]; then echo "Usage: $0 " >&2 echo "Example: $0 108800 Crysis2.exe" >&2 exit 2 fi APPID="$1" PROCESS_NAME="$2" START_TIMEOUT="${START_TIMEOUT:-120}" EXIT_STABLE_TIME="${EXIT_STABLE_TIME:-3}" LOG_DIR="${XDG_STATE_HOME:-${HOME}/.local/state}/sunshine" LOG_FILE="${LOG_DIR}/steam-game.log" mkdir -p "${LOG_DIR}" WRAPPER_PID="$$" log() { printf '[%s] [STEAM:%s] %s\n' \ "$(date '+%Y-%m-%d %H:%M:%S')" \ "${APPID}" \ "$*" >> "${LOG_FILE}" } # # Retourne les PID dont la ligne de commande contient littéralement # le nom de processus demandé. # game_pids() { local proc local pid local cmdline local -a argv for proc in /proc/[0-9]*; do pid="${proc##*/}" # Ne jamais matcher le wrapper principal. [[ "${pid}" == "${WRAPPER_PID}" ]] && continue # game_pids() tourne dans un sous-shell à cause de : # # mapfile -t pids < <(game_pids) # # Il faut donc également ignorer ce sous-shell. [[ "${pid}" == "${BASHPID}" ]] && continue [[ -r "${proc}/cmdline" ]] || continue argv=() mapfile -d '' -t argv < "${proc}/cmdline" 2>/dev/null || true [[ ${#argv[@]} -gt 0 ]] || continue cmdline="${argv[*]}" if [[ "${cmdline}" == *"${PROCESS_NAME}"* ]]; then printf '%s\n' "${pid}" fi done } # # Termine le ou les processus correspondant au jeu. # terminate_game() { local -a pids mapfile -t pids < <(game_pids) if [[ ${#pids[@]} -eq 0 ]]; then log "No game process to terminate" return 0 fi log "Stopping game processes: ${pids[*]}" kill -TERM "${pids[@]}" 2>/dev/null || true sleep 3 mapfile -t pids < <(game_pids) if [[ ${#pids[@]} -gt 0 ]]; then log "Force stopping remaining processes: ${pids[*]}" kill -KILL "${pids[@]}" 2>/dev/null || true fi } # # Si Sunshine termine le wrapper, terminer également le jeu. # on_termination() { log "Wrapper received termination signal" terminate_game exit 143 } trap on_termination TERM INT HUP # # Vérifications. # if ! command -v steam >/dev/null 2>&1; then log "ERROR: steam command not found" exit 127 fi log "Wrapper started with PID ${WRAPPER_PID}" log "Launching Steam AppID ${APPID}" log "Process name: ${PROCESS_NAME}" # # Demander à Steam de lancer le jeu. # steam -applaunch "${APPID}" \ >> "${LOG_FILE}" 2>&1 & # # Attendre l'apparition du véritable processus du jeu. # start_time="${SECONDS}" while true; do mapfile -t pids < <(game_pids) if [[ ${#pids[@]} -gt 0 ]]; then log "Game detected: ${pids[*]}" break fi if (( SECONDS - start_time >= START_TIMEOUT )); then log "ERROR: Game did not appear within ${START_TIMEOUT} seconds" exit 124 fi sleep 1 done # # Rester vivant aussi longtemps que le jeu. # missing_since="" while true; do mapfile -t pids < <(game_pids) if [[ ${#pids[@]} -gt 0 ]]; then missing_since="" else if [[ -z "${missing_since}" ]]; then missing_since="${SECONDS}" log "Game process disappeared, waiting ${EXIT_STABLE_TIME}s" fi if (( SECONDS - missing_since >= EXIT_STABLE_TIME )); then log "Game exited" break fi fi sleep 1 done log "Wrapper exiting normally" exit 0