Steam
Steam 是桌面目标下的一个发行渠道,不是一个单独的平台。它不定义运行时、不定义渲染器、 不定义资源格式——它定义的是这份构建去哪儿。所以没有“Steam 构建”这回事:只有桌面构建, 外加一个开关,让导出顺带写出 SteamPipe 需要的东西。
桌面构建是原生应用——引擎的 C++ 核心,内嵌 Dawn(macOS 走 Metal,Windows 走 D3D12,Linux 走 Vulkan)与 QuickJS-ng,窗口与输入来自 SDL3。进程里没有浏览器, 这也是 Steam 覆盖层、Steam Input 和独占全屏能如玩家预期那样工作的原因。
-
一个 Steamworks 应用。 合作伙伴后台给的 App ID——depot、启动选项和成就都住在那里, 任何构建都写不到。
-
Steamworks SDK,从合作伙伴站点自行下载。Estella 不随包携带:它的再分发只授权给 Steamworks 合作伙伴,而运行时模板是所有用这个引擎的游戏共用的同一个预编译二进制。
-
steamcmd,Valve 的上传工具。先在终端里登录一次,让 Steam Guard 通过并缓存下来—— Estella 不保存任何密码。
打开 文件 → Build…,在目标列表里选 Steam——它列在「桌面」下面。Steam 是桌面构建的 一个去处,所以它从那个目标分叉出来,而不是与它并列。这个渠道要填的东西都在这一页上。

| 设置 | 含义 |
|---|---|
| 发行渠道 | 选 Steam——构建旁边会写出 depot 脚本。在目标列表里点 Steam 就是在设它,两者是同一个值。 |
| Steam App ID | 来自合作伙伴后台。不填就什么也不写:用猜出来的 id 生成的脚本指向的是别人的游戏。 |
| Steamworks SDK | 你下载的那份 SDK。它 redistributable_bin/<os>/ 下的库会被打进应用。 |
| Depot ID | 留 0 则用 App ID + 1、+2…。这些由 Valve 分配,生成的清单会提示你去核对。 |
成就 id 不在这一页,它在 项目设置 → 打包 里——因为每个商店都有这份同样的清单, 只是名字不同。声明它们,才能让「解锁一个后台根本没有的 id」在发生处就被拒绝, 而不是被接受、然后悄无声息地丢掉。
然后点 打包。应用旁边会得到:
dist-desktop/ MyGame.app/ ← macOS,以及/或者 MyGame/ ← Windows:MyGame.exe + Content/ ← Linux: MyGame + Content/ steam/ app_build_<appid>.vdf depot_<id>_<os>.vdf STEAM.md ← 本次构建的确切取值值得读的是 STEAM.md。通用说明属于本文这样的文档;查不到的是这一个构建需要把什么粘到哪里——
每个 OS 的启动可执行文件、这次用的 depot id、游戏真正写入的 Auto-Cloud 路径,以及要去后台建的成就 id。
steamcmd +login <你的账号> +run_app_build "…/steam/app_build_<appid>.vdf" +quit脚本里写着 Preview "1",所以第一次跑什么也不会上传——它只报告它会做什么。要真上传就把它去掉。
SetLive 是刻意留空的:发布到某个分支是另一个决定,在后台做,而且那正是无法靠“再传一次”撤销的那个。
Achievements 与 Ads、Leaderboard 一样是引擎服务,所以有没有商店,游戏代码都一样:
import { Achievements } from 'esengine';
const achievements = world.getResource(Achievements);achievements.setStat('enemies_defeated', count);await achievements.unlock('FIRST_BLOOD');await achievements.store(); // 推到 Steam;商店在这一步后面做批量提交它永远可用。背后没有商店时,解锁记在本地——那正是你自己的成就界面要读的同一份数据——
而 available 告诉你商店是否也会收到,UI 据此决定是用平台的弹窗还是自己画。
成就静默失效有两种方式,引擎各自的处理:
- 后台没有这个 id。 Steam 会接受它然后什么也不做,最后是玩家在几个月后发现。所以 id 在项目设置里声明一次,越界的 unlock 在发生处就被拒绝,并把已声明的清单一并报出来。
- 包里没有 Steam 库。 把 SDK 那一项指向你的下载;没有的话导出会警告,
STEAM.md开头也会写明。
Storage 写在操作系统指定的位置,而那正是 Steam Auto-Cloud 能同步的路径,游戏侧一行代码都不用写。
在 Steam Cloud → Auto-Cloud 里填 STEAM.md 打印出来的根目录——它们是照应用实际写到哪得出的,
不是抄自某份文档里的表:
| OS | 根目录 | 子路径 |
|---|---|---|
| Windows | WinAppDataRoaming |
Estella/<名字> |
| macOS | MacHome |
Library/Application Support/Estella/<名字> |
| Linux | LinuxHome |
.local/share/Estella/<名字> |
- macOS 构建是 ad-hoc 签名的,而且只能在 Mac 上签。公开发布前请用你自己的 Developer ID 公证。
steamcmd没有做进编辑器。 清单里给出的是可以照抄的确切命令;一个在这里验证不了的功能, 不如一行你能复制的命令诚实。- 创意工坊、DLC、内购、Steam Input 动作集不在覆盖范围内。它们都能挂在同一套 flat API 和同一个 服务面上,但每一个都是自己的需求,不该由“支持 Steam 发布”一次性带出来。