概览
Steamworks API 让您的游戏可以访问通过此 API 提供的所有基础系统,充分利用 Steam 的所有功能, 包括用户打开
Steam 界面时暂停您的游戏、邀请好友游戏、允许玩家解锁
Steam 成就、让玩家在
Steam 排行榜上竞争,等等。
Steamworks API 索引对 API 中支持的每个接口、功能、回调和类型进行了分类和记录。
在 Steam 上发行产品从不要求与 Steamworks API 集成,但我们强烈推荐您进行集成,因为这样您可以实现 Steam 用户期待的许多交互。
入门指南
注意: Steamworks API 官方支持 C++,在 Microsoft 的 Windows 上使用 Microsoft Visual Studio 2008+,在 macOS 和 SteamOS / Linux 上使用 GCC 4.6+ 和 Clang 3.0+。 如果您使用的是第三方引擎或非 C++ 编程语言,您应该先查看
商用引擎和非 C++ 语言支持,了解上手使用您的引擎或所选择的语言是否有更多特定说明。 有时候您也许可以跳过许多步骤。
- 如果你尚未下载 Steamworks SDK,请下载 Steamworks SDK 并解压缩。
- 将 Steamworks API 头文件夹
public/steam
复制至您应用程序源树中的恰当位置。
- 将相关的可再发行文件从
redistributable_bin
复制至项目文件夹的恰当位置。
- Windows
您必须在自己的 Visual Studio 项目中有 steam_api[64].lib
链接。 可以链接到主要的可执行文件或使用 Steam 的模块上, 这样您可以访问 steam_api[64].dll
内通过 Steamworks API 头文件暴露的功能。 更多信息请参阅:将可执行文件链接到 DLL
您还需要在您的运行时目录中发布 steam_api[64].dll
(位于您的程序可执行文件旁,或在您的 DLL 搜索路径中)。
- macOS
libsteam_api.dylib
提供了 Steam API 的 x86 和 x64 版本。 您必须将其链接至您的 Xcode 项目中,并与您的可执行文件一起发布。
更多信息请参阅:Using Dynamic Libraries
- Linux
您必须将 libsteam_api.so
链接至您的可执行文件,并一起发布。
初始化与关闭
SteamAPI_Init
您在自己的项目内设置 Steamworks API 后,就可以通过调用
SteamAPI_Init 函数,初始化此 API 并开始使用。 这样即可设置全局状态,并填入可以通过与此接口名称匹配的全局函数访问的接口指针。
必须调用此函数并返回成功,才能访问任何
Steamworks 接口!
如果 Steamworks API 不知晓您游戏的 AppID 则不会初始化。 您从 Steam 启动自己的应用时,Steam 会自动准备好 AppID。 您在开发时需要用文本文件提示 Steam。 在您的可执行文件旁创建名为
steam_appid.txt
的文本文件,其中只包含 AppID,不含有任何其他内容。 这样会覆盖 Steam 提供的值。 您不应该将其与生成版一起发布。 示例:
480
返回
false 表明发生了以下情况之一:
- Steam 客户端未运行。 需要有运行的 Steam 客户端才能提供各种 Steamworks 接口的实现。
- Steam 客户端无法判定游戏的 App ID。 如果您直接通过可执行文件或调试器运行您的应用程序,那么您的游戏目录中的可执行文件旁,必须有一个
steam_appid.txt
,其中只记录了您的应用 ID,此外不含有任何其他内容。 Steam 将在当前工作目录中,查找此文件。 如果您从不同的目录中运行可执行文件,您也许需要重新定位 steam_appid.txt
文件。
- 您的应用程序运行的 OS 用户上下文,与 Steam 客户端并不相同,比如用户或管理员访问权限级别不同。
- 确定您在当前活跃的 Steam 帐户中拥有该 App ID 的许可。 您的游戏必须显示在您的 Steam 库中。
- 您的 AppID 未完全设置,如
发行状态:不可用
,或缺失默认程序包。
如果您遇到初始化问题,请查看
Steamworks API 调试文档,了解有关调试 Steamworks API 的各种方法。
SteamAPI_RestartAppIfNecessary
SteamAPI_RestartAppIfNecessary 将查看您的可执行文件是否通过 Steam 启动,如果不是,则重启可执行文件。
此项非必选,但强烈建议使用。因为如果用户直接启动可执行文件,与您的应用程序(包括您的 AppID)关联的 Steam 上下文不会被设置。 如果发生这种情况,
SteamAPI_Init
会失败,您将无法使用 Steamworks API。
如果选择使用此项,则您应该在调用
SteamAPI_Init前,首先调用此 Steamworks 函数。
如果返回
true,则在必须时启动 Steam 客户端,然后通过客户端再次启动您的游戏,而您应尽快退出进程。 此举将有效地运行
steam://run/<AppID>
,因此可能不会重启调用此函数的确切可执行文件(例如,如果您是从调试程序运行可执行文件)。 将始终从安装在您 Steam 库文件夹中的版本进行重启。
而如果返回
false,则您的游戏是由 Steam 客户端启动,无需进行任何操作。 一个例外是,如果存在
steam_appid.txt
文件,则无论何种情况都会返回
false。 这让您不必通过 Steam 客户端启动您的游戏,便能进行开发与测试。 将游戏上传至您的 Steam depot 时,务必删除
steam_appid.txt
!
注意: 如果您在您的主要可执行文件上使用 Steam DRM 包装器,则此检查步骤并非必需,因为 DRM 包装器会内部进行检查。
SteamAPI_Shutdown
使用 Steamworks API 完毕后,您应该调用
SteamAPI_Shutdown 以释放 Steam 内您的应用程序所使用的资源。 如果可能,您应该在进程关闭时调用此函数。
这将不会取消
Steam 界面与您游戏的挂钩,因为不能保证您的渲染 API 已将其使用完毕。
Steamworks 接口
Steamworks API 由多个接口组成,均公开了有限的、特定数量的功能。
Steamworks 成功初始化后,您可以通过其全局函数访问这些接口。 函数名称与接口名称始终匹配。 因此您可以通过
SteamApps()
访问器访问
ISteamApps,也可以通过
SteamFriends()
访问
ISteamFriends。
您可以使用这些接口进行如下调用:
// 获得当前用户的 Steam 名称。
const char *name = SteamFriends()->GetPersonaName();
您可以在
Steamworks API 索引上,或是打开 Steamworks API 头文件查看完整的接口列表。
回调
回调是 Steamworks 最重要的方面之一,让您可以在不锁定游戏的情况下从 Steam 异步获取数据。 回调的目的是提供简单、轻巧、类型安全、线程安全的方式,引发异步事件至注册为侦听器的任何对象。
回调通常由来自 Steam 的事件(例如好友登录或退出),或某些 API 函数的异步结果触发。 每个回调由一个结构组成,其中包含一个唯一的标识符和一小组数据。
ISteam*.h
头文件中声明了回调,与其最紧密所属的接口归为一组。
要侦听回调,结构或类必须在其声明中使用宏
STEAM_CALLBACK( :classname, :functionname, :callbackname )
。
-
:classname
必须为要在其中进行定义的结构或类的名称。 (如 CGameManager
)
-
:functionname
为接收此回调的函数名称。 (如 OnGameOverlayActivated
)
-
:callbackname
为回调的名称。 (如 GameOverlayActivated_t
)
这为该类定义了已自动保持原型为
void :functionname( :callbackname *pCallback )
的一个本地成员函数。 创建对象的一个新实例将导致此成员函数将其自身注册为 Steamworks API 的侦听器;销毁对象将导致其取消注册。
注意: 您应该确保创建侦听回调的对象前,Steam 已初始化。
针对要调度至已注册侦听器的回调,您必须为其调用
SteamAPI_RunCallbacks。 最好能频繁调用,因为调用间隔时间越长,从 Steamworks API 接收事件或结果的延迟越长。 大部分游戏每渲染一帧调用一次,我们强烈建议您至少每秒调用一次。 调用期间,在调用
SteamAPI_RunCallbacks
的线程上下文中,所有注册侦听器函数都会被调用。
示例
一个您也许希望使用的回调是
ISteamFriends::GameOverlayActivated_t。 正如其名称所暗示,每次用户激活或停用
Steam 界面时,它都会给您发送一个回调。
class CGameManager
{
private:
STEAM_CALLBACK( CGameManager, OnGameOverlayActivated, GameOverlayActivated_t );
};
void CGameManager::OnGameOverlayActivated( GameOverlayActivated_t* pCallback )
{
if ( pCallback->m_bActive )
printf( "Steam overlay now active\n" );
else
printf( "Steam overlay now inactive\n" );
}
ISteamFriends::GameOverlayActivated_t 回调的一个常见也建议使用的情况是打开界面时,暂停游戏。
CallResults
许多 Steamworks 方法使用调用结果,而不是回调,来异步返回函数调用的结果。 回调与调用结果的区别在于,回调向所有侦听器广播, 而调用结果只针对特定侦听器。 与回调一样,您的游戏需要调用
SteamAPI_RunCallbacks,以将调用结果调度至侦听者。
您可以通过查看其返回的值来识别提供调用结果的函数。如果返回
SteamAPICall_t 且具有
CALL_RESULT()
属性,则您必须注册以接收调用结果。
注意: 回调与调用结果不可互换。 事件只能通过其中一种方法传入,而非两者均可。 您必须确保注册的事件类型正确!
调用结果必须用 CCallResult 类型创建为结构/类的成员,您还需要创建接收回调的成员函数。
void func( :callresultname *pCallback, bool bIOFailure );
CCallResult< :classname, :callresultname > m_callresultname;
-
:classname
必须为要在其中进行定义的结构或类的名称。 (如 CGameManager
)
-
:callresultname
为回调的名称。 (如 NumberOfCurrentPlayers_t
)
函数、函数参数以及 CCallResult 类型可按需自由命名。
示例
以下示例显示了如何使用会产生
ISteamUserStats::NumberOfCurrentPlayers_t 调用结果的
ISteamUserStats::GetNumberOfCurrentPlayers API。
// 在您的类定义中
class CGameManager
{
public:
void GetNumberOfCurrentPlayers();
private:
void OnGetNumberOfCurrentPlayers( NumberOfCurrentPlayers_t *pCallback, bool bIOFailure );
CCallResult< CGameManager, NumberOfCurrentPlayers_t > m_NumberOfCurrentPlayersCallResult;
};
// 进行异步请求,以接收当前玩家数量。
void CGameManager::GetNumberOfCurrentPlayers()
{
printf( "Getting Number of Current Players\n" );
SteamAPICall_t hSteamAPICall = SteamUserStats()->GetNumberOfCurrentPlayers();
m_NumberOfCurrentPlayersCallResult.Set( hSteamAPICall, this, &CGameManager::OnGetNumberOfCurrentPlayers );
}
// 调用 SteamAPI_RunCallbacks() 后,在.SteamUserStats()->GetNumberOfCurrentPlayers() 异步返回时调用。
void CGameManager::OnGetNumberOfCurrentPlayers( NumberOfCurrentPlayers_t *pCallback, bool bIOFailure )
{
if ( bIOFailure || !pCallback->m_bSuccess )
{
printf( "NumberOfCurrentPlayers_t failed!\n" );
return;
}
printf( "Number of players currently playing: %d\n", pCallback->m_cPlayers );
}
注意: 如果您不能使用 CCallResult 系统,那么您也许可以使用
ISteamUtils::IsAPICallCompleted、
ISteamUtils::GetAPICallResult 和
ISteamUtils::GetAPICallFailureReason 来追踪调用结果的状态。
手动回调调度
用来注册回调的类和宏在 C++ 代码中方便好用。 但还有一个更低级别的机制用来处理回调。 此机制操作起来更像一个 Windows 事件循环。 您在一个循环中提取下一个可用的回调,然后用自己希望的任何机制进行调度,而不是调度所有回调及调用结果来激活侦听器的一个单一函数。 手动调用模式对于绑定将 Steamworks SDK 暴露至 C++ 之外其他语言的层尤其有用。 参见
steam_api.h
中的
SteamAPI_ManualDispatch_Init()
,了解更多信息。
Steam 游戏服务器
Steamworks API 对运行游戏服务器和常规客户端都提供支持。 在 Steamworks API 术语中,游戏服务器是普通用户为进行多人游戏而连接的系统中实体, 可以通过互联网连接至远程游戏服务器,或本地连接至与客户端处于同一进程中的游戏服务器。 游戏服务器有自己的 API 函数集可用,也有自己独一无二的 Steam ID,便于其他用户引用。
使用 Steam 游戏服务器 API 时,您首先需要纳入
steam_gameserver.h
,而非
steam_api.h
。
初始化并使用游戏服务器 API 与普通 API 非常相似:
初始化游戏服务器后,您即可访问两个游戏服务器独有的接口
ISteamGameServer 和
ISteamGameServerStats。
您也可以从游戏服务器访问以下常规接口:
如果您运行的是专用游戏服务器(没有客户端组件),您只需要初始化游戏服务器 API,无需初始化普通户 API。
请参见
Steamworks API 示例应用程序(SpaceWar),了解使用游戏服务器 API 的完整载入示例。
与游戏不同的是,专用服务器通常会在没有安装 Steam 客户端的环境中运行,因而无法获得最新的 Steamworks 二进制文件。 为了让专用服务器获得最新的 Steam 二进制文件,您必须在您的应用中纳入专用服务器可再发行文件。登录
partner.steamgames.com 并导航至您应用的技术设置,然后在“安装”->“可再发行文件”中查看并勾选“专用服务器可再发行文件”。
商用引擎和非 C++ 语言支持
如果您使用的是商用游戏引擎或非 C 或 C++ 语言,您需要查看其对 Steamworks API 提供了什么水平的支持。
有些引擎提供原生内置支持,有些则可能需要第三方解决方案。
如果您的引擎没有原生支持,您可以使用
Steam Web API 来访问 Steam 支持的多个功能。
无论 Steamworks SDK 以何种方式集成至您的引擎,您必须要有最新的 Steamworks SDK,以
将您的应用程序上传至 Steam。
注意: 如果您在 Steam 上发布的软件中部分可以通过限制性开源许可获得,那么请参见
在 Steam 上分销开源应用程序。
下方列出了大家常用于在 Steam 上发布游戏的常见引擎,以及如何在这些引擎中上手使用 Steamworks API 的相关文档。
注意: Valve 不以任何方式对这些引擎中的任何之一或任何第三方解决方案提供担保或支持。 此列表完全按名称排序,远未包含所有引擎,只起入门指南之用。 只有包含原生支持或所提供第三方解决方案符合准则的引擎才列入其中。 只有按照 Steamworks SDK 适度更新、可根据宽松许可免费获得(请见
在 Steam 上分销开源应用程序),且在
Steamworks 讨论版有讨论串的第三方解决方案才列入其中。 建议您咨询社区,了解哪个引擎最适合您的特定设置。
引擎:
语言:
- ActionScript(Adobe Flash, AIR)
- C#
- D
- Java
- JavaScript
- Python
- Rust
绑定至其他语言的扁平化接口
SDK 有一些功能可以辅助创建其他语言的绑定层。
-
steam_api_flat.h
声明了一个在 SDK 中对接口函数进行镜像的“扁平化”函数集。 这并非纯 C 代码,但确实使用普通 C 链接及调用约定,因此容易与其他语言进行互操作。 这些函数由 steam_api[64][.dll/.so/dylib]
导入。
-
steam_api.json
描述了 SDK 中的(几乎所有)接口、类型及函数, 旨在由自动进程来使用此文件生成绑定层。 我们希望这可以用来自动完成 95% 的工作,但还是有一些特殊情况需要手动处理, 尤其是 CSteamID 和 CGameID 可能需要您的绑定层来特殊处理,才能更为高效。
技术详情
Steam 使用多种技术将功能暴露给您的应用程序。 您完全不需要彻底了解 Steamworks API 的工作原理,不过,这其实还是相当简单的,而且能有助于您就如何让所编写的游戏代码与 Steam 进行集成做出计划。
您链接至
steam_api[64][.dll/.so/dylib]
后,它将提供对
一小部分 C 语言函数的访问权限,这些函数前缀为
S_API
宏,均在
steam_api.h
和
steam_gameserver.h
中暴露。
steam_api
模块本身很小,只暴露基本功能,以初始化和关闭 Steamworks API。
Steamworks API 在初始化时,会找到正在运行的 Steam 客户端进程,并从该路径载入
steamclient.dll
。
steamclient.dll
本质上是 Steam 客户端的核心引擎, 包含并维护运行 Steam 所需的信息。 Steam 客户端 UI 用与此处暴露的函数类似的一个函数集访问
steamclient.dll
提供的数据。 由于此数据在 Steam 进程中,因此所有 Steam API 调用都透明封送并通过 RPC/IPC 机制发送 (跨进程管道
ISteamClient::HSteamPipe)。
Steam 进程通过
steamclient.dll
保持与 Steam 后端服务器的持续连接。 所有验证、匹配、好友列表与 VAC 通信均通过此连接发生。 如果用户有网络问题,或者如果用户连接至的 Steam 服务器要更新,此连接会断开。 如果连接断开或重新连接,回调将发布至任何运行的应用。 Steam 客户端会自动尝试重新连接,且正在运行应用的客户端有优先权,因此,重新连接时间一般不超过 10 秒,但在一些罕见的情况下,可能会长达 5 分钟。
Steamworks API 通过类似 COM 的机制进行版本管理,接口的字符串名称与版本传入
steamclient.dll
,后者再向调用方返回正确的接口版本。
steamclient.dll
包含一套适用于接口所有已发行版本的适配器,在最新接口的上下文中重新定向或解释旧调用。 适配器位于 Steam 客户端内,如果出现任何问题,很容易更新。