Sunshine 应用添加实战指南:从桌面到游戏启动的配置示例与命令体系详解
【免费下载链接】SunshineSelf-hosted game stream host for Moonlight.项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine
本文档是给 Moonlight 客户端用户添加可串流应用(Application)的配置实战指南,围绕 Sunshine 仓库中 docs/app_examples.md 的核心示例展开。读者在 Web UI 的 Applications(应用)管理页添加条目时,将掌握桌面直连、Steam / Epic 启动器 URI 与二进制三种启动方式,以及针对不同桌面环境与显卡的分辨率切换 Prep Command(预备命令)写法,并理解各字段在 src/process.cpp 中的实际执行语义。
目录
- 理解应用配置的核心概念
- 通用示例:桌面与 Steam Big Picture
- 游戏启动器示例:Epic 与 Steam 游戏
- Prep Commands:分辨率与刷新率切换
- 附加考虑事项
- 总结与进一步阅读
一、理解应用配置的核心概念
1.1 为什么要「逐应用」配置
并非所有应用都以相同的方式启动:桌面环境、Steam 自带自更新进程、Epic 启动器需要通过 URL Scheme 唤醒游戏、有的游戏二进制必须带工作目录运行。Sunshine 因此在应用条目中提供了若干灵活字段,从而让主机的输出行为(分辨率、刷新率、HDR)与「应用是否仍在运行」的判定完全可控。
1.2 需要理解的关键字段
在 src/confighttp.cpp 的saveApp接口注释中,Sunshine 完整定义了应用条目的 JSON 结构,字段说明如下:
| 字段 | 类型 | 含义 |
|---|---|---|
name | 字符串 | 应用显示名(Application Name) |
output | 字符串 | 日志输出文件路径(Log Output Path) |
cmd | 字符串 | 应用启动命令(Command)。为空表示启动桌面(Desktop) |
index | 整数 | 保存位置索引,-1表示新建,更新已有应用时传入其当前索引 |
exclude-global-prep-cmd | 布尔 | 是否跳过 Sunshine 配置文件中global_prep_cmd定义的所有全局预备命令(见 docs/configuration.md 的global_prep_cmd小节) |
elevated | 布尔 | 该应用命令是否以管理员权限启动 |
auto-detach | 布尔 | 应用启动后 5 秒内即优雅退出时,是否将其视为脱离型(detached)命令继续串流 |
wait-all | 布尔 | 是否等待整个进程组退出(而非只等待初始进程)才算应用结束 |
exit-timeout | 整数 | 关闭应用时,向进程组发送终止信号后等待的秒数 |
prep-cmd | 数组 | 预备命令数组,每项含do(启动前执行)、undo(应用退出后执行)与可选的elevated |
detached | 数组 | 需要分离式启动(不阻塞应用运行状态判定)的命令,每项为一个命令字符串 |
image-path | 字符串 | 应用图标路径,必须是 png 文件(可填desktop.png、steam.png等仓库内置图标) |
working-dir | 字符串 | 应用工作目录,留空时默认指向目标程序所在目录 |
1.3 默认工作目录与「空命令 = 桌面」的底层逻辑
原文给出了两条重要约定,均可在源码中得到印证:
- Working Directory 缺省时,Sunshine 默认将其设置为目标应用所在目录。在 src/process.cpp 中,无论是
do/undo预备命令(第 224-226 行)还是 detached 命令(第 253-255 行)、主cmd(第 269-271 行),都执行了同一逻辑:_app.working_dir.empty() ? find_working_directory(cmd, _env) : path(_app.working_dir)。 cmd留空时 Sunshine 不会启动任何进程,而是进入「桌面占位」模式(placebo = true,见 src/process.cpp),此时串流目标是整个桌面,应用持续「运行」直到客户端断开。
此外,Sunshine 还会在启动应用时向子进程注入一组SUNSHINE_*环境变量(src/process.cpp),包括:
SUNSHINE_APP_ID、SUNSHINE_APP_NAME:当前应用标识SUNSHINE_CLIENT_NAME:客户端名称SUNSHINE_CLIENT_WIDTH、SUNSHINE_CLIENT_HEIGHT、SUNSHINE_CLIENT_FPS、SUNSHINE_CLIENT_HDR:客户端本次请求的分辨率、帧率与 HDR 开关SUNSHINE_CLIENT_GCMAP、SUNSHINE_CLIENT_AUDIO_CONFIGURATION(2.0/5.1/7.1)等音频配置
这些环境变量是下文中所有「动态分辨率切换」命令的数据来源。
二、通用示例:桌面与 Steam Big Picture
2.1 Desktop(桌面直连)
当你只想把整个桌面画面串流给 Moonlight 时,添加如下条目(原文的@code{}记号在 Web UI 中即对应相应输入框内容):
| 字段 | 值 |
|---|---|
| Application Name | Desktop |
| Image | desktop.png |
对应的apps.json条目与仓库中预置的 src_assets/linux/assets/apps.json 完全一致:应用名Desktop、无cmd,图片为desktop.png。该示例文件同时给出了一个「Low Res Desktop」变体,用prep-cmd在启动前把显示器切到 1920x1080、退出后还原到 1920x1200,可以作为最简单的 Prep Command 参考:
{ "name": "Low Res Desktop", "image-path": "desktop.png", "prep-cmd": [ { "do": "xrandr --output HDMI-1 --mode 1920x1080", "undo": "xrandr --output HDMI-1 --mode 1920x1200" } ] }2.2 Steam Big Picture(大屏模式)
Steam 启动后其主进程会被自更新进程替换并退出,因此必须以 detached(分离式)命令启动,并用prep-cmd的undo负责在串流结束后关闭大屏模式。各平台字段如下:
| 平台 | Application Name | Command Preparations → Undo | Detached Commands |
|---|---|---|---|
| FreeBSD / Linux | Steam Big Picture | setsid steam steam://close/bigpicture | setsid steam steam://open/bigpicture |
| macOS | Steam Big Picture | open steam://close/bigpicture | open steam://open/bigpicture |
| Windows | Steam Big Picture | steam://close/bigpicture | steam://open/bigpicture |
原仓库示例 src_assets/linux/assets/apps.json 中的 Steam Big Picture 条目即采用此写法,image-path为steam.png:
{ "name": "Steam Big Picture", "detached": [ "setsid steam steam://open/bigpicture" ], "prep-cmd": [ { "do": "", "undo": "setsid steam steam://close/bigpicture" } ], "image-path": "steam.png" }理解这一结构的代码语义:在 src/process.cpp 中,detached数组中的每条命令都会通过run_command启动后立即调用child.detach()脱离管理,主进程是否存活不会影响串流会话;而prep-cmd中的do(启动前)与undo(应用退出后)是同步等待执行的。值得注意do留空会被跳过(if (cmd.do_cmd.empty()) continue;,见 src/process.cpp),这正是上面条目中do为空的原因——Steam 的开启动作交给 detached 完成。
[!TIP] 本示例相关的单元测试可在 tests/unit/test_process.cpp 中查看。
三、游戏启动器示例:Epic 与 Steam 游戏
原文强调:使用启动器的 URI 方式最稳定一致,因为它不依赖游戏二进制路径随商店更新而变化。
3.1 Epic Game Store 游戏(以《Surviving Mars》为例)
URI 方式
| 字段 | 值 |
|---|---|
| Application Name | Surviving Mars |
| Commands | com.epicgames.launcher://apps/d759128018124dcabb1fbee9bb28e178%3A20729b9176c241f0b617c5723e70ec2d%3AOvenbird?action=launch&silent=true |
其中路径段:AppCatalogItemId:AppItemId(冒号被编码为%3A)由 Epic 启动器为每个游戏生成,可在启动器中获取。
Binary(带工作目录)
| 字段 | 值 |
|---|---|
| Application Name | Surviving Mars |
| Command | MarsEpic.exe |
| Working Directory | "C:\Program Files\Epic Games\SurvivingMars" |
Binary(不带工作目录)
| 字段 | 值 |
|---|---|
| Application Name | Surviving Mars |
| Command | "C:\Program Files\Epic Games\SurvivingMars\MarsEpic.exe" |
后两者对比直观演示了工作目录规则:如果你不填 Working Directory,就必须把 Command 写成完整可执行文件路径,让 Sunshine 依据「默认工作目录 = 程序所在目录」的规则(见上文 src/process.cpp)自行推断;若提供了 Working Directory,则 Command 只需写可执行文件名。
3.2 Steam 游戏(以《Surviving Mars》,Steam AppID 464920 为例)
URI 方式(推荐)
| 平台 | Detached Commands |
|---|---|
| FreeBSD / Linux | setsid steam steam://rungameid/464920 |
| macOS | open steam://rungameid/464920 |
| Windows | steam://rungameid/464920 |
所有平台只需填 Detached Commands,无需 prep 命令——和 Steam Big Picture 一样,因为 Steam 主进程自更新的特性要求分离式启动。
Binary(带工作目录)
| 字段 | FreeBSD / Linux / macOS 值 | Windows 值 |
|---|---|---|
| Command | MarsSteam | MarsSteam.exe |
| Working Directory | $(HOME)/.steam/steam/SteamApps/common/Surviving Mars | "C:\Program Files (x86)\Steam\steamapps\common\Surviving Mars" |
[!NOTE] Linux/macOS 的 Steam 默认库位于
$(HOME)/.steam/steam/SteamApps(对应 Windows 的C:\Program Files (x86)\Steam\steamapps),若你安装了额外的 Steam 库,请把路径替换为实际安装目录。原文保留了路径中Survivng Mars的拼写,配置时以你机器上的实际目录名为准。
Binary(不带工作目录)
Command 直接写完整可执行文件路径:
| 平台 | Command |
|---|---|
| FreeBSD / Linux / macOS | $(HOME)/.steam/steam/SteamApps/common/Surviving Mars/MarsSteam |
| Windows | "C:\Program Files (x86)\Steam\steamapps\common\Surviving Mars\MarsSteam.exe" |
四、Prep Commands:分辨率与刷新率切换
游戏/桌面运行时往往需要把显示器临时切换为客户端请求的分辨率与帧率(尤其是不同步的物理显示与无线串流场景),串流结束后再还原。Sunshine 为此在每个应用条目中提供了prep-cmd数组,并在运行时通过SUNSHINE_CLIENT_WIDTH / HEIGHT / FPS / HDR环境变量(见 src/process.cpp)把客户端请求值传给命令。do命令在应用启动前同步执行,若失败则中止本次启动(返回非零退出码会令proc_t::start()返回 -1,见 src/process.cpp);undo命令在会话结束后执行,用于恢复物理显示器。
以下按平台与显示服务器整理原文给出的完整命令表。
4.1 Linux
X11(通用及 GNOME/X11)
| Prep Step | Command |
|---|---|
| Do | sh -c "xrandr --output HDMI-1 --mode ${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} --rate ${SUNSHINE_CLIENT_FPS}" |
| Undo | xrandr --output HDMI-1 --mode 3840x2160 --rate 120 |
[!TIP] 上述命令仅在 xrandr 模式已存在时有效。macOS 与 iOS 客户端使用非标准分辨率,通常需要先动态创建新模式。可以将
do换成调用自定义脚本:bash -c "${HOME}/scripts/set-custom-res.sh \"${SUNSHINE_CLIENT_WIDTH}\" \"${SUNSHINE_CLIENT_HEIGHT}\" \"${SUNSHINE_CLIENT_FPS}\""脚本
set-custom-res.sh内容如下(可存至~/scripts/并chmod +x):#!/bin/bash set -e # Get params and set any defaults width=${1:-1920} height=${2:-1080} refresh_rate=${3:-60} # You may need to adjust the scaling differently so the UI/text isn't too small / big scale=${4:-0.55} # Get the name of the active display display_output=$(xrandr | grep " connected" | awk '{ print $1 }') # Get the modeline info from the 2nd row in the cvt output modeline=$(cvt ${width} ${height} ${refresh_rate} | awk 'FNR == 2') xrandr_mode_str=${modeline//Modeline \"*\" /} mode_alias="${width}x${height}" echo "xrandr setting new mode ${mode_alias} ${xrandr_mode_str}" xrandr --newmode ${mode_alias} ${xrandr_mode_str} xrandr --addmode ${display_output} ${mode_alias} # Reset scaling xrandr --output ${display_output} --scale 1 # Apply new xrandr mode xrandr --output ${display_output} --primary --mode ${mode_alias} --pos 0x0 --rotate normal --scale ${scale} # Optional reset your wallpaper to fit to new resolution # xwallpaper --zoom /path/to/wallpaper.png该脚本用
cvt生成目标分辨率的 modeline、创建新模式后施加到当前活动显示器,并通过--scale调节 UI 缩放比例。需要恢复壁纸时可取消最后一行xwallpaper的注释。
Wayland(wlroots 系合成器,如 Hyprland)
| Prep Step | Command |
|---|---|
| Do | sh -c "wlr-xrandr --output HDMI-1 --mode \"${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}@${SUNSHINE_CLIENT_FPS}Hz\"" |
| Undo | wlr-xrandr --output HDMI-1 --mode 3840x2160@120Hz |
[!TIP]
wlr-xrandr仅适用于 wlroots 系合成器(Hyprland、Sway 等),其余 Wayland 合成器请使用下列专门方案。
GNOME(Wayland)
| Prep Step | Command |
|---|---|
| Do | sh -c "displayconfig-mutter set --connector HDMI-1 --resolution ${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} --refresh-rate ${SUNSHINE_CLIENT_FPS} --hdr ${SUNSHINE_CLIENT_HDR}" |
| Undo | displayconfig-mutter set --connector HDMI-1 --resolution 3840x2160 --refresh-rate 120 --hdr false |
- 需要先安装
displayconfig-mutter工具,相关安装说明请查看其上游仓库。可替代工具包括gnome-randr-rust与gnome-randr.py,但两者均已停止维护,且不支持较新 Mutter 的 HDR、VRR 特性,故不推荐。 - HDR 支持自 GNOME 48 起提供。可用
displayconfig-mutter list检查显示器是否支持 HDR;若不支持,请将do与undo命令中的--hdr参数一并移除。
KDE Plasma(Wayland 与 X11)
| Prep Step | Command |
|---|---|
| Do | sh -c "kscreen-doctor output.HDMI-A-1.mode.${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}@${SUNSHINE_CLIENT_FPS}" |
| Undo | kscreen-doctor output.HDMI-A-1.mode.3840x2160@120 |
[!CAUTION] X11 与 Wayland 下的显示器名称可能不同,例如同一台显示器在 X11 叫
HDMI-A-0,在 Wayland 下可能叫HDMI-A-1,务必根据当前会话类型使用正确的名称。
[!TIP] 用
kscreen-doctor -o可列出所有可用显示器及其支持的属性,然后将命令中的HDMI-A-1替换为你要用于 Moonlight 的显示器名。你也可以硬编码显示器模式编号(如kscreen-doctor output.HDMI-A1.mode.0),或用上面的do命令动态采用 Moonlight 客户端请求的分辨率(该值有一定概率不被物理显示器支持)。
NVIDIA
| Prep Step | Command |
|---|---|
| Do | sh -c "nvidia-settings -a CurrentMetaMode=\"HDMI-1: nvidia-auto-select { ViewPortIn=${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}, ViewPortOut=${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT}+0+0 }\"" |
| Undo | nvidia-settings -a CurrentMetaMode="HDMI-1: nvidia-auto-select { ViewPortIn=3840x2160, ViewPortOut=3840x2160+0+0 }" |
4.2 macOS
使用displayplacer工具切换分辨率(该工具由 jakehilborn 维护,需按其在 GitHub 仓库的说明安装)。
| Prep Step | Command |
|---|---|
| Do | sh -c "displayplacer \"id:<screenId> res:${SUNSHINE_CLIENT_WIDTH}x${SUNSHINE_CLIENT_HEIGHT} hz:${SUNSHINE_CLIENT_FPS} scaling:on origin:(0,0) degree:0\"" |
| Undo | displayplacer "id:<screenId> res:3840x2160 hz:120 scaling:on origin:(0,0) degree:0" |
命令中的<screenId>请替换为运行displayplacer list输出的目标显示器 ID。
4.3 Windows
Sunshine 在 Windows 上内置了分辨率/刷新率切换能力(相关实现见 src/platform/windows/display_base.cpp、display_ram.cpp 等文件),并非必须使用第三方工具;若希望用外部工具,可参考 QRes(从 SourceForge 下载):
| Prep Step | Command |
|---|---|
| Do | cmd /C "FullPath\qres.exe /x:%SUNSHINE_CLIENT_WIDTH% /y:%SUNSHINE_CLIENT_HEIGHT% /r:%SUNSHINE_CLIENT_FPS%" |
| Undo | FullPath\qres.exe /x:3840 /y:2160 /r:120 |
注意 Windows 下环境变量引用采用%VAR%语法(而非 Unix 的${VAR}),且需把FullPath\qres.exe替换为 QRes 的实际安装路径。
五、附加考虑事项
5.1 Linux(Flatpak 环境)
[!CAUTION] Flatpak 包运行在沙箱中,默认无法访问宿主机。因此Sunshine 的 Flatpak 版本要求所有命令以
flatpak-spawn --host作为前缀,例如flatpak-spawn --host xrandr ...,否则命令无法操作宿主的显示输出。
5.2 Windows:提升命令权限(Elevating Commands)
如果将 Sunshine 作为 Windows 服务运行(默认方式),应用可能需要在无 UAC 弹窗的情况下以管理员权限执行命令。可在 Web UI 中勾选相应选项,或在apps.json中为命令添加"elevated": true。该选项同时适用于普通cmd/prep-cmd,进程会以当前登录用户身份被提升启动(对应 src/platform/windows/misc.cpp 中retrieve_users_token的令牌获取逻辑)。完整示例:
{ "name": "Game With AntiCheat that Requires Admin", "output": "", "cmd": "ping 127.0.0.1", "exclude-global-prep-cmd": false, "elevated": true, "prep-cmd": [ { "do": "powershell.exe -command \"Start-Streaming\"", "undo": "powershell.exe -command \"Stop-Streaming\"", "elevated": false } ], "image-path": "" }运行时elevated在 src/confighttp.cpp 中被从旧版本常用的字符串"true"/"false"规范化为真正的布尔值(同一处理还覆盖exclude-global-prep-cmd、auto-detach、wait-all与整数型的exit-timeout);随后在配置解析时被填充为 src/config.h 定义的prep_cmd_t{ do_cmd, undo_cmd, elevated },并最终传入platf::run_command(elevated, ...)执行。
5.3 相关代码路径速查
若想从源码层面验证以上行为,可按下列路径深入:
- 应用条目的 JSON 完整格式与保存逻辑:src/confighttp.cpp(
saveApp) prep-cmd结构体定义(do_cmd/undo_cmd/elevated):src/config.h- 应用启动时序:prep
do→ detached → 主cmd→ 应用退出后反向执行 prepundo:src/process.cpp 与 src/process.cpp(terminate) - 客户端请求注入的环境变量(
SUNSHINE_CLIENT_*):src/process.cpp auto-detach的 5 秒优雅退出判定:src/process.cpp- 仓库内置的应用示例文件:src_assets/linux/assets/apps.json
- 其它平台内置
apps.json:src_assets/macos/assets/apps.json、src_assets/windows/assets/apps.json
六、总结与进一步阅读
本文从「为什么要逐应用配置」出发,介绍了 Sunshine 应用条目的全部核心字段,逐一演示了桌面、Steam Big Picture、Epic 与 Steam 游戏在 URI / 二进制两种启动方式下的完整填法,并系统整理了各桌面环境与显卡在 X11、Wayland、macOS、Windows 下的分辨率切换 Prep Command。需要特别记住的三条规则是:工作目录缺省指向程序所在目录、cmd留空即为桌面直连、Steam 系必须走 detached 启动。结合源码可见这些字段都对应 src/process.cpp 中明确的执行分支,理解后可大幅提升排查启动异常的效率。
关于 Sunshine 全部配置项(含global_prep_cmd等)的完整说明见 docs/configuration.md;本文中命令用到的SUNSHINE_CLIENT_*环境变量的注入逻辑可回溯 src/process.cpp。若需要更多第三方应用案例,可继续阅读 docs/awesome_sunshine.md 社区资源清单。
【免费下载链接】SunshineSelf-hosted game stream host for Moonlight.项目地址: https://gitcode.com/GitHub_Trending/su/Sunshine
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考