| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176 |
- #!/usr/bin/env bash
- set -u
- if [[ $# -lt 2 ]]; then
- echo "Usage: $0 <Steam AppID> <process name>" >&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
|