Files
a13labs.infra/ansible/playbook_game_station.yml
T
alexandre.pires b4f4fe2f07 Refactor SteamOS session management and launcher scripts
- Removed obsolete launch_desktop.sh and launch_session.sh scripts.
- Introduced new steam-launcher script to handle Steam launching and session tracking.
- Added steam-short-session-tracker for monitoring short Steam sessions and triggering repairs.
- Created steamos and steamos-session scripts for managing the SteamOS session.
- Implemented steamos-session-select for switching between Gamescope and Plasma sessions.
- Updated SDDM configuration for autologin and session management.
- Added new systemd services and targets for managing Gamescope and Steam sessions.
- Created desktop entries and icons for ALVR and SteamOS shortcuts.
- Enhanced logging and error handling across scripts.
- Cleaned up unused systemd slice and service files.
- Updated Ansible playbook to reflect new configurations and file structures.
2025-12-01 17:25:18 +01:00

519 lines
16 KiB
YAML

---
- name: Configure Fedora 42 for optimized Sunshine + Gamescope streaming
hosts: game_station
vars:
user_name: "gameuser"
system_bin_dir: "/usr/local/bin"
user_config_dir: "/home/{{ user_name }}/.config"
user_bin_dir: "/home/{{ user_name }}/.local/bin"
systemd_user_dir: "/home/{{ user_name }}/.config/systemd/user"
systemd_system_dir: "/etc/systemd/system"
grub_cfg: "/etc/default/grub"
screen_resolution: "{{ lookup('env', 'GAME_RESOLUTION') | d('1920x1080', true) }}"
screen_width: "{{ screen_resolution.split('x')[0] }}"
screen_height: "{{ screen_resolution.split('x')[1] }}"
hdr_enabled: "{{ lookup('env', 'HDR_ENABLED') | d('false', true) }}"
output_connector: "{{ lookup('env', 'OUTPUT_CONNECTOR') | d('HDMI-A-1', true) }}"
framerate: "{{ lookup('env', 'FRAMERATE') | d('60', true) }}"
drm_device: "{{ lookup('env', 'DRM_DEVICE') | d('/dev/dri/card1', true) }}"
grub_options:
- quiet
- splash
- amd_pstate=active
- nvidia_drm.modeset=1
- fbdev=1
- mitigations=off
- threadirqs
- drm.edid_firmware=DP-1:edid/aoc28e850.bin
- video=DP-1:e
- acpi_enforce_resources=lax
nvidia_modules:
- nvidia
- nvidia_modeset
- nvidia_uvm
- nvidia_drm
appimages:
- name: Steam ROM Manager
url: "https://github.com/SteamGridDB/steam-rom-manager/releases/download/v2.5.30/Steam-ROM-Manager-2.5.30.AppImage"
executable: "SteamRomManager.AppImage"
link: "srm"
- name: RPCS3
url: "https://github.com/RPCS3/rpcs3-binaries-linux/releases/download/build-cc8929ef4a6142fa5b240d2f5ec19ead54cc6efa/rpcs3-v0.0.38-18287-cc8929ef_linux64.AppImage"
executable: "rpcs3.AppImage"
link: "rpcs3"
- name: Eden
url: "https://github.com/eden-emulator/Releases/releases/download/v0.0.4-rc1/Eden-Linux-v0.0.4-rc1-amd64-clang-standard.AppImage"
executable: "Eden.AppImage"
link: "eden"
alvr_version: "20.14.1"
tasks:
# --- Base dependencies ---
- name: Enable COPR repositories
become: true
community.general.copr:
name: "{{ item }}"
state: enabled
loop:
- lizardbyte/stable
- atim/heroic-games-launcher
- name: Ensure core gaming and streaming packages are installed
become: true
ansible.builtin.dnf:
name:
- gamescope
- Sunshine
- steam
- mangohud
- kernel-tools
- util-linux
- mesa-dri-drivers
- vulkan-tools
- jq
- xorg-x11-drv-nvidia
- xorg-x11-drv-nvidia-cuda
- xorg-x11-drv-nvidia-libs
- vulkan
- vulkan-loader
- nvidia-settings
- libepoxy
- weston
- bubblewrap
- waypipe
- drm-utils
- ethtool
- heroic-games-launcher-bin
- libva-nvidia-driver
state: present
- name: Ensure user namespaces are enabled
become: true
ansible.posix.sysctl:
name: user.max_user_namespaces
value: "15000"
state: present
sysctl_set: true
reload: true
- name: Ensure bubblewrap is setuid root (Fedora default)
become: true
ansible.builtin.file:
path: /usr/bin/bwrap
owner: root
group: root
mode: "04755"
- name: Enable cpupower at boot
become: true
ansible.builtin.systemd:
name: cpupower
enabled: true
state: started
- name: Create firmware directory for EDID files
become: true
ansible.builtin.file:
path: /lib/firmware/edid
state: directory
owner: root
group: root
mode: "0755"
- name: Copy custom EDID file
become: true
ansible.builtin.copy:
dest: "/lib/firmware/edid/aoc28e850.bin"
owner: root
group: root
mode: "0644"
content: "{{ lookup('file', 'game_station/firmware/aoc28e850.bin') | b64decode }}"
- name: Ensure EDID file is available in initramfs
become: true
ansible.builtin.copy:
dest: /etc/dracut.conf.d/90-edid.conf
owner: root
group: root
mode: "0644"
content: |
install_items+=" /lib/firmware/edid/aoc28e850.bin "
notify: Run dracut
- name: Ensure NVIDIA modules are loaded at boot
become: true
ansible.builtin.copy:
dest: /etc/modules-load.d/nvidia.conf
owner: root
group: root
mode: "0644"
content: |
{% for module in nvidia_modules %}
{{ module }}
{% endfor %}
notify: Run dracut
- name: Determine default network interface
ansible.builtin.set_fact:
primary_iface: "{{ ansible_default_ipv4.interface }}"
- name: Get NetworkManager connection name for primary interface
become: true
ansible.builtin.command: >
bash -lc "nmcli -t -f NAME,DEVICE,TYPE c show --active |
awk -F: -v IF='{{ primary_iface }}' '$2==IF && $3==\"802-3-ethernet\"{print $1; exit}'"
register: nm_conn_name
changed_when: false
failed_when: nm_conn_name.stdout == ""
- name: Ensure WOL is persistent (magic) on the NM connection
become: true
ansible.builtin.shell: >
set -euo pipefail;
cur=$(nmcli -g 802-3-ethernet.wake-on-lan connection show "{{ nm_conn_name.stdout }}" | tr -d '[:space:]');
if [ "$cur" != "magic" ]; then
nmcli connection modify "{{ nm_conn_name.stdout }}" 802-3-ethernet.wake-on-lan magic;
echo changed;
fi
register: nm_wol_set
changed_when: "'changed' in nm_wol_set.stdout"
- name: Apply WOL for current boot
become: true
ansible.builtin.command: "ethtool -s {{ primary_iface }} wol g"
changed_when: false
failed_when: false
# --- Performance kernel options ---
- name: Tune kernel command line for isolation
become: true
ansible.builtin.lineinfile:
path: "{{ grub_cfg }}"
regexp: "^GRUB_CMDLINE_LINUX_DEFAULT="
line: 'GRUB_CMDLINE_LINUX_DEFAULT="{{ grub_options | join(" ") }}"'
notify: Update grub
- name: Ensure cpupower governor is performance
become: true
ansible.builtin.command: cpupower frequency-set -g performance
changed_when: false
failed_when: false
- name: Set default target to graphical
become: true
ansible.builtin.command: systemctl set-default graphical.target
changed_when: false
- name: Disable Unwanted services
become: true
ansible.builtin.systemd:
name: "{{ item }}"
enabled: false
state: stopped
loop:
- gdm.service
- ModemManager.service
- cups.service
- chronyd.service
- NetworkManager-wait-online.service
- smartd.service
# System actions
- name: Install Systemd system files
become: true
ansible.builtin.copy:
src: "{{ item }}"
dest: "{{ systemd_system_dir }}/{{ item | basename }}"
mode: "0644"
owner: root
group: root
loop: "{{ query('fileglob', 'game_station/systemd/system/*') }}"
- name: Reload systemd daemon for system services
become: true
ansible.builtin.systemd:
daemon_reload: true
- name: Install System scripts
become: true
ansible.builtin.copy:
src: "{{ item }}"
dest: "{{ system_bin_dir }}/{{ item | basename }}"
owner: root
group: root
mode: "0755"
loop: "{{ query('fileglob', 'game_station/bin/system/*') }}"
- name: Create system directories
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: root
group: root
mode: "0755"
loop:
- /root/.local/logs
- name: Copy wayland session files for SDDM
become: true
ansible.builtin.copy:
src: "{{ item }}"
dest: "/usr/share/wayland-sessions/{{ item | basename }}"
owner: root
group: root
mode: "0644"
loop: "{{ query('fileglob', 'game_station/wayland-sessions/*') }}"
- name: Install SDDM configuration for auto-login
become: true
ansible.builtin.template:
src: "{{ item }}"
dest: "/etc/sddm.conf.d/{{ item | basename }}"
owner: root
group: root
mode: "0644"
loop: "{{ query('fileglob', 'game_station/sddm.conf/*') }}"
- name: Create log file for Jupiter BIOS update (dummy file)
become: true
ansible.builtin.file:
path: /root/.local/logs/jupiter-biosupdate.log
state: file
owner: root
group: root
mode: "0644"
# User actions
- name: Create game group
become: true
ansible.builtin.group:
name: "{{ user_name }}"
state: present
- name: Create dedicated game user
become: true
ansible.builtin.user:
name: "{{ user_name }}"
groups:
- "{{ user_name }}"
- wheel
- video
- audio
- input
shell: /bin/zsh
create_home: true
state: present
- name: Add user to sudoers with no password
become: true
ansible.builtin.copy:
dest: "/etc/sudoers.d/{{ user_name }}"
owner: root
group: root
mode: "0440"
content: "{{ user_name }} ALL=(ALL) NOPASSWD: ALL"
- name: Create directories
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0755"
loop:
- "{{ user_bin_dir }}"
- "{{ systemd_user_dir }}"
- "{{ user_config_dir }}"
- "{{ user_config_dir }}/gamescope"
- "{{ user_config_dir }}/sunshine"
- "{{ user_config_dir }}/steam-rom-manager/userData"
- "/home/{{ user_name }}/.local/logs"
- "/home/{{ user_name }}/.local/run"
- "/home/{{ user_name }}/.local/share/applications"
- "/home/{{ user_name }}/.local/share/icons"
- "/home/{{ user_name }}/Emulation"
- "/home/{{ user_name }}/Emulation/snes"
- "/home/{{ user_name }}/Emulation/psp"
- "/home/{{ user_name }}/Emulation/genesis"
- "/home/{{ user_name }}/RetroArch/shaders"
- "/home/{{ user_name }}/RetroArch/config"
- "/opt/steamcmd"
- "/opt/alvr"
- name: Install Systemd user files
become: true
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ systemd_user_dir }}/{{ item | basename }}"
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0644"
loop: "{{ query('fileglob', 'game_station/systemd/user/*') }}"
- name: Reload systemd daemon for user services
become: true
become_user: "{{ user_name }}"
ansible.builtin.systemd:
daemon_reload: true
scope: user
- name: Install AppImages
become: true
ansible.builtin.get_url:
url: "{{ item.url }}"
dest: "{{ user_bin_dir }}/{{ item.executable }}"
mode: "0755"
owner: "{{ user_name }}"
group: "{{ user_name }}"
loop: "{{ appimages }}"
- name: Create symlink for AppImages
become: true
ansible.builtin.file:
src: "{{ user_bin_dir }}/{{ item.executable }}"
dest: "{{ user_bin_dir }}/{{ item.link }}"
state: link
owner: "{{ user_name }}"
group: "{{ user_name }}"
loop: "{{ appimages }}"
- name: Download steamtools
become: true
ansible.builtin.get_url:
url: "https://steamcdn-a.akamaihd.net/client/installer/steamcmd_linux.tar.gz"
dest: "/tmp/steamcmd_linux.tar.gz"
mode: "0644"
owner: "{{ user_name }}"
group: "{{ user_name }}"
register: steamcmd_download
- name: Extract steamtools
become: true
ansible.builtin.unarchive:
src: "/tmp/steamcmd_linux.tar.gz"
dest: "/opt/steamcmd"
remote_src: true
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0755"
- name: Create symlink for Steam logs
become: true
ansible.builtin.file:
src: "/home/{{ user_name }}/.steam/steam/logs"
dest: "/home/{{ user_name }}/.local/logs/steam"
state: link
owner: "{{ user_name }}"
group: "{{ user_name }}"
- name: Copy steam-rom-manager configurations
become: true
ansible.builtin.copy:
src: "game_station/srm/userConfigurations.json"
dest: "/home/{{ user_name }}/.config/steam-rom-manager/userData/userConfigurations.json"
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0644"
- name: Install User scripts
become: true
ansible.builtin.copy:
src: "{{ item }}"
dest: "{{ user_bin_dir }}/{{ item | basename }}"
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0755"
loop: "{{ query('fileglob', 'game_station/bin/user/*') }}"
- name: Write Gamescope configuration
become: true
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ user_config_dir }}/gamescope/{{ item | basename }}"
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0644"
loop: "{{ query('fileglob', 'game_station/gamescope/*') }}"
- name: Install Sunshine files
become: true
ansible.builtin.template:
src: "{{ item }}"
dest: "{{ user_config_dir }}/sunshine/{{ item | basename }}"
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0644"
loop: "{{ query('fileglob', 'game_station/sunshine/*') }}"
# ALVR installation
- name: Install ALVR
ansible.builtin.get_url:
url: "https://github.com/alvr-org/ALVR/releases/download/v{{ alvr_version }}/alvr_streamer_linux.tar.gz"
dest: "/tmp/alvr_streamer_linux.tar.gz"
mode: "0644"
- name: Extract ALVR
become: true
ansible.builtin.unarchive:
src: "/tmp/alvr_streamer_linux.tar.gz"
dest: "/opt/alvr"
remote_src: true
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0755"
extra_opts:
- --strip-components=1
- name: Create symlink for ALVR
become: true
ansible.builtin.file:
src: "/opt/alvr/bin/alvr_dashboard"
dest: "{{ user_bin_dir }}/alvr_dashboard"
state: link
owner: "{{ user_name }}"
group: "{{ user_name }}"
- name: Copy shortcuts to remote host
become: true
ansible.builtin.copy:
src: "{{ item }}"
dest: "/home/{{ user_name }}/.local/share/applications/{{ item | basename }}"
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0644"
loop: "{{ query('fileglob', 'game_station/shortcuts/*.desktop') }}"
- name: Copy icons to remote host
become: true
ansible.builtin.copy:
src: "{{ item }}"
dest: "/home/{{ user_name }}/.local/share/icons/{{ item | basename }}"
owner: "{{ user_name }}"
group: "{{ user_name }}"
mode: "0644"
loop: "{{ query('fileglob', 'game_station/shortcuts/icons/*.png') }}"
- name: Enable Required services
become: true
ansible.builtin.systemd:
name: "{{ item }}"
enabled: true
state: started
loop:
- sddm.service
handlers:
- name: Update grub
ansible.builtin.command: grub2-mkconfig -o /boot/grub2/grub.cfg
become: true
changed_when: false
- name: Run dracut
ansible.builtin.command: dracut --regenerate-all --force
become: true
changed_when: false