Документация Steamworks
Обзор API Steamworks

Введение

API Steamworks предоставляет играм все преимущества Steam благодаря доступу ко всем основным системам с помощью API. Сюда входят, к примеру, постановка игры на паузу в момент открытия пользователем оверлея Steam, приглашение друзей в игру, достижения Steam, соревнование игроков в списках лидеров Steam и так далее.

В разделе Справочник по API Steamworks задокументированы все интерфейсы, функции, обратные вызовы и типы, поддерживаемые в API.

Интеграция с API Steamworks не требуется для доставки продуктов пользователям Steam, но мы рекомендуем её реализацию, поскольку она позволяет удовлетворить ожидания пользователей Steam.

С чего начать

ВНИМАНИЕ: API Steamworks официально поддерживает C++, использование Microsoft Visual Studio 2008+ на Microsoft Windows, GCC 4.6+ и Clang 3.0+ на macOS и SteamOS / Linux. Если вы используете сторонние движки или отличные от C++ языки программирования, вам следует ознакомиться с разделом Commercial engine and non-C++ language support, где могут быть изложены инструкции, относящиеся к вашему движку или выбранному языку. В некоторых случаях вы можете пропустить некоторые из нижеследующих инструкций.

  • Если вы этого ещё не сделали, скачайте SDK Steamworks и распакуйте zip-файл.
  • Скопируйте папку заголовочных файлов public/steam в подходящее место в исходном дереве кода вашего приложения.
  • Copy the relevant redistributable files from redistributable_bin into an appropriate place in your project folder.
    • Windows
      You must have steam_api[64].lib linking in your visual studio project. This can be linked to either the primary executable or a module that uses Steam. This allows you to access the functionality contained in steam_api[64].dll which is exposed via the Steamworks API headers. Additional Reading: Linking an Executable to a DLL (MSDN)

      You must also ship the steam_api[64].dll in your run-time directory (next to your programs executable, or in your dll search path).
    • macOS
      libsteam_api.dylib provides both the x86 and x64 version of the Steam API. You must link to this in your Xcode project and you must ship this along side your executable.
      Additional Reading: Using Dynamic Libraries
    • Linux
      libsteam_api.so You must both link to and ship this along side your executable.

Инициализация и завершение работы

SteamAPI_Init

После того, как вы настроите API Steamworks в своём проекте, вы можете начать использовать его, вызывая SteamAPI_Init для инициализации. Это настроит глобальное состояние и заполнит его указателями интерфейсов, доступных с помощью глобальных функций, соответствующих названиям интерфейсов. Вызов SteamAPI_Init и получение сообщения о его успехе ОБЯЗАТЕЛЬНЫ до выполнения вызовов к другим интерфейсам!

API Steamworks не будет инициализирован, если ему не будет известен номер вашего приложения (App ID). При запуске приложения в Steam он автоматически получит номер приложения. Во время разработки вы должны сообщить его в виде текстового файла. Создайте текстовый файл steam_appid.txt, который будет содержать исключительно номер приложения, в папке с исполняемым файлом. Это число будет иметь приоритет над тем значением, которое предоставляется Steam. Нельзя отправлять этот файл с вашими сборками. К примеру:
480

Возвращает false при следующих условиях:
  • Не запущен клиент Steam. Запуск клиента Steam обязателен для корректной работы интерфейсов Steamworks.
  • Клиенту Steam не удалось определить App ID игры. Если вы запускаете приложение напрямую из исполняемого файла или отладчика, в папке игры должен находится файл steam_appid.txt, в котором должен присутствовать исключительно номер приложения. Steam попытается найти этот файл в текущей папке. Если вы запускаете исполняемый файл из другой папки, вам может потребоваться переместить steam_appid.txt.
  • Ваше приложение запущено не с тем же пользовательским контекстом в операционной системе, что и клиент Steam, к примеру, одно запущено под обычным пользователем, а второе — с правами администратора.
  • Убедитесь, что активный аккаунт Steam владеет приложением. Игру должно быть видно в вашей библиотеке Steam.
  • Приложение не настроено полностью, к примеру, Release State: Unavailable, или в нём отсутствуют комплекты по умолчанию.
Если у вас возникают трудности, прочтите раздел «Отладка API Steamworks», чтобы узнать о различных методах отладки API Steamworks.

SteamAPI_RestartAppIfNecessary

SteamAPI_RestartAppIfNecessary проверяет, запущен ли исполняемый файл через Steam, и если нет, перезапускает его в Steam.

Это необязательно, но крайне рекомендуется, поскольку контекст Steam (включая номер приложения) не будет настроен, если пользователь запустит исполняемый файл напрямую. Если это случится, вызов SteamAPI_Init не состоится, и вы не сможете использовать API Steamworks.
Если вы решите его использовать, это должен быть первый вызов Steamworks, выполняемый непосредственно перед SteamAPI_Init.

Если возвращается true, будет запущен клиент Steam (если это необходимо), а затем через клиент и игра. Вы должны завершить свой процесс как можно скорее. В этот момент запускается steam://run/<AppID>, так что необязательно будет запущен тот исполняемый файл, который вызвал эту функцию (к примеру, если вы запускали из отладчика). Всегда будет перезапускаться версия, установленная в папке с библиотекой Steam.

Если же будет возвращено значение false, игра была запущена клиентом, и никаких действий предпринимать не нужно. В то же время, если файл steam_appid.txt имеется в наличии, этот вызов всегда будет возвращать false. Это позволяет разрабатывать и тестировать игру без необходимости запускать её из клиента. Не забудьте удалить steam_appid.txt перед отправкой игры в хранилище Steam!

ВНИМАНИЕ: если вы используете оболочку DRM для первичного исполняемого файла, эта проверка необязательна, поскольку оболочка выполнит её самостоятельно.

SteamAPI_Shutdown

Когда вы завершите использование API Steamworks вы должны вызвать SteamAPI_Shutdown, чтобы освободить ресурсы, используемые приложением в Steam. Если возможно, вы должны сделать этот вызов при завершении процесса.

Этот вызов не отключит оверлей от приложения, поскольку нет гарантии, что ваш API выведения изображений перестал его использовать.

Интерфейсы Steamworks

API Steamworks состоит из множества интерфейсов, которые предоставляют ограниченную, конкретную функциональность.

После того, как API был успешно инициализирован, вы можете получить доступ интерфейсам с помощью их глобальных функций. Названия функций всегда соответствуют названиям интерфейсов. Так, вы можете получить доступ к ISteamApps с помощью SteamApps() и к ISteamFriends с помощью SteamFriends().

Вы можете использовать интерфейсы для подобных вызовов:
// Получить имена профилей Steam текущих пользователей. const char *name = SteamFriends()->GetPersonaName();

Вы можете увидеть полный список интерфейсов здесь или изучив заголовочные файлы API Steamworks.

Обратные вызовы

Обратные вызовы — один из самых важных элементов Steamworks, который позволяет получать данные из Steam асинхронно, без необходимости блокировать игру. Цель обратных вызовов в том, чтобы предоставить простой, легкий, типобезопасный, потокобезопасный метод представления событий любому объекту, зарегистрированному в качестве слушателя.

Обратные вызовы обычно срабатывают, когда то или иное событие происходит в Steam, к примеру когда друг входит в сеть или выходит из неё, или в результате функций API. Каждый обратный вызов состоит из структуры (struct), содержащей уникальный идентификатор и небольшой набор данных. Обратные вызовы объявлены в заголовочных файлах ISteam*.h и сгруппированы по интерфейсам, которым они принадлежат в наибольшей степени.

Чтобы слушать обратный вызов, структура или класс должны иметь в объявлении следующий макрос: STEAM_CALLBACK( :classname, :functionname, :callbackname ).
  • :classname — название структуры или класса, где определяется вызов (к примеру, CGameManager).
  • :functionname — название функции, получающей обратный вызов (к примеру, OnGameOverlayActivated).
  • :callbackname — название обратного вызова (к примеру, GameOverlayActivated_t).
Это определяет локальную функцию-член данного класса, которая автоматически получает прототип void :functionname( :callbackname *pCallback ). Создание нового экземпляра данного объекта приведёт к регистрации функции-члена как слушателя в API Steamworks. Уничтожение объекта приведет к отмене регистрации.

ВНИМАНИЕ: Обратите внимание: вы должны убедиться, что Steam инициализирован до того, как создавать объекты, которые слушают обратные вызовы.

Чтобы подготовить отправку обратных вызовов зарегистрированным слушателям, необходимо вызывать SteamAPI_RunCallbacks. Лучше всего делать это часто, поскольку чем больше времени проходит между вызовами, тем больше задержка между получением событий и результатами от API Steamworks. Большинство игр вызывают это один раз за одну отрисовку кадра. Рекомендуется вызывать как минимум раз в секунду. Во время вызова будут вызваны все функции, зарегистрированные как слушатели в контексте потока, где был отправлен вызов SteamAPI_RunCallbacks.

Пример

Один из обратных вызовов, который вы, скорее всего, будете использовать, — это ISteamFriends::GameOverlayActivated_t. Как и подразумевает его название, он отправляется всякий раз, когда пользователь открывает или закрывает оверлей.

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 часто используется для того, чтобы при открытии оверлея поставить игру на паузу.

Результаты вызова

Многие методы 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::GetNumberOfCurrentPlayers, которая производит результат вызова ISteamUserStats::NumberOfCurrentPlayers_t.
// В определении класса 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 ); } // Вызывается, когда SteamUserStats()->GetNumberOfCurrentPlayers() возвращается асинхронно, после вызова SteamAPI_RunCallbacks(). 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 для отслеживания состояния результата вызова.

Manual Callback Dispatch

The classes and macros used to register callbacks are convenient in C++ code. But there is also a lower-level mechanism for dealing with callbacks. This mechanism operates more like a windows event loop. Instead of a single function that dispatches all of the callbacks and call results to active listeners, you fetch the next available callback in a loop, and dispatch it using whatever mechanisms you want. The manual dispatch mode is especially useful for binding layers that expose the Steamworks SDK to languages other than C++. See SteamAPI_ManualDispatch_Init() in steam_api.h for more information.

Steam Game Servers

The Steamworks API includes support for running game servers as well as regular clients. A game server, in Steamworks API terms, is an entity in the system that normal users connect to to play multi-player games. This can be connecting over the Internet to a remote game server, or connecting locally to a game server that's in the same process as the client. Game servers have their own set of API functions to use, and their own unique Steam ID for other users to refer to them by.

To use the Steam Game Server API you must first include steam_gameserver.h instead of steam_api.h.

Initializing and using the game server API is very similar to the normal API:

After initializing a game server you have access to the two game server exclusive interfaces ISteamGameServer and ISteamGameServerStats.

You can also access the following regular interfaces from the game server:
  • ISteamClient — с помощью глобального интерфейса SteamGameServerClient()
  • ISteamUtils — с помощью глобального интерфейса SteamGameServerUtils()
  • ISteamNetworking — с помощью глобального интерфейса SteamGameServerNetworking()
  • ISteamHTTP — с помощью глобального интерфейса SteamGameServerHTTP()
  • ISteamUGC which you can access via the global interface: SteamGameServerUGC()
  • ISteamApps which you can access via the global interface: SteamGameServerApps()

If you're running a dedicated game server (one which has no client component), you only have to initialize the game server API's, you don't have to initialize the normal user API.

See Приложение-образец с API Steamworks (SpaceWar) for a fully loaded example of using the game server API.

Unlike a game, a dedicated server will usually be run in an environment where there is no Steam Client installed to provide the latest steamworks binaries. In order for the dedicated server to have the latest steam binaries, you will need to include the Dedicated Server Redistributables with your app. Log into partner.steamgames.com and navigate to the technical settings for your app. Then look under "Installation / Redistributables" and check "Dedicated Server Redistributables"

Commercial engine and non-C++ language support

If you're using a commercial game engine or a language other than C or C++ you will want to see what level of support for the Steamworks API is provided for it.
In some engines they provide native built-in support, for others you may need a third-party solution.

If your engine does not have native support you can use the Steam Web API to access many features that Steam supports.

Regardless of how the Steamworks SDK is implemented in your engine you will need to have the latest Steamworks SDK to upload your application to Steam.

ВНИМАНИЕ: If some of the software you are shipping on steam is available under a restrictive open source license then please see Распространение приложений с открытым кодом в Steam.

Here are some common engines that people routinely use to ship games on Steam and the relevant documentation on how to get started using the Steamworks SDK with them.

ВНИМАНИЕ: Valve does not in any way endorse any of these engines or the third-party solutions. This list is sorted exclusively by name, and is in no way comprehensive, it exists solely as a starting guide. Engines will only be listed if they include native support or have a third-party solution that fits the guidelines. Third-party solutions will only be listed if they are kept reasonably up to date with the Steamworks SDK, are freely available under a permissive license (See: Распространение приложений с открытым кодом в Steam), and have a thread on the Steamworks Discussion Board. We recommend that you consult the community to see which option would work best for your specific setup.

Движки:

ДвижокВстроенная поддержка?Информация
CRYENGINE ✔️
GameMaker Studio 2 ✔️ Использование SDK Steamworks с GameMaker: Studio
Godot Third Party: GodotSteam - Steamworks Developer Discussions support thread
Haxe Сторонняя: SteamWrapфорум разработчиков Steamworks
Leadwerks Game Engine ✔️ Leadwerks API Reference › Steamworks
RPG Maker MV Сторонняя: Greenworksфорум разработчиков Steamworks
Source 2013 ✔️ Distributing Source Engine Games / Mods
Unity Сторонняя: Facepunch.Steamworks - форум разработчиков Steamworks
Сторонняя: http://steamworks.github.io - форум разработчиков Steamworks
Unreal Engine 4 ✔️ Online Subsystem Steam
Visionaire Studio ✔️ Публикация в Steam

Языки:

Flat interface for binding to other languages

The SDK has a few features to facilitate the creation of binding layers for other languages.

  • steam_api_flat.h declares a set of "flat" functions that mirror the interface functions in the SDK. This is not pure C code, but it does use plain C linkage and calling conventions, so it is easy to interop with other languages. These functions are exported by steam_api[64][.dll/.so/dylib].
  • steam_api.json describes (almost all of) the interfaces, types, and functions in the SDK. It is intended that this file be used by an automated process to generate binding layer. We hope that this can be used to automate 95% of the work, but there are still a few special cases that need to be handled manually. In particular, CSteamID and CGameID will probably require special handling by your binding layer to make it efficient.

Technical Details

Steam uses a variety of techniques to expose functionality to your application. It's by no means critical to understand exactly how the Steamworks API works but it's fairly simple and can be useful in planning out how to code your game with Steam integration in mind.

When you link to steam_api[64][.dll/.so/dylib] it provides access to a small number of C functions which are prefixed with the S_API macro, these functions are all exposed in steam_api.h and steam_gameserver.h. The steam_api module itself is very small, it only exposes the base functionality to initialize and shutdown the Steamworks API.

When the Steamworks API initializes it finds the actively running steam client process and loads steamclient.dll from that path. steamclient.dll is essentially the core engine of the Steam client. It contains and maintains the information necessary to run Steam. The Steam client UI uses a similar set of functions as exposed here to access the data provided by steamclient.dll. Since this data is in the Steam process, all Steam API calls are transparently marshaled and sent via an RPC/IPC mechanism. (A cross-process pipe, ISteamClient::HSteamPipe).

The Steam process, via steamclient.dll maintains a constant connection to the Steam back-end servers. Through this connection all authentication, matchmaking, friends list and VAC communication occurs. This connection may drop if the user has a network issue, or if the Steam server they are connected to receives an update. Callbacks will be posted to any running app if the connection drops or re-establishes. The Steam client will automatically try to reconnect, and clients running an app receive priority, so the typical reconnect time is under 10 seconds, although in some rare cases may be up to 5 minutes.

The Steamworks API is versioned through a COM-like mechanism, where a string name and version of an interface are passed into steamclient.dll which then returns the correct version of the interface to the caller. steamclient.dll contains a set of adapters for all released versions of an interface which redirect or re-interpret the old calls in the context of the latest interface. The adapters live inside the steam client so that they are easy to update if any issues arise.