SteamConnectionSystem
Namespace: Vortex.Steam.SteamConnectionSystem
Сборка: ru.vortex.steam.connection
Платформа: Unity 2021.3+, Steamworks.NET
Условная компиляция: код за #if USING_STEAM, сборка без defineConstraints
Назначение
Подключение к Steamworks API. Инициализирует Steam-клиент, предоставляет глобальное состояние подключения и данные текущего пользователя (профиль, список друзей) через статическую шину SteamBus.
Возможности:
SteamBus— статическая шина:SteamEnabled,IsInitialized,IsLoaded,User, событияOnCallServices/OnLoadedSteamManager— MonoBehaviour-синглтон:SteamAPI.Init(),RunCallbacks(),Shutdown()SteamUserData/SteamUserShortData— модели данных пользователя и друзейSteamConnectionSettings— ScriptableObject-конфигурация:steamAppId,isEnabled,isTestBuildDefineSymbolManager— автоматическое управление символомUSING_STEAMвPlayerSettingsSettings— загрузка/создание ассета настроек, синхронизацияsteam_appid.txt
Вне ответственности:
- Достижения — пакет
SteamAchievements - Мультиплеер, матчмейкинг, лобби
- Покупки, DLC, инвентарь
Зависимости
| Зависимость | Назначение |
|---|---|
Steamworks.NET |
SteamAPI, SteamUser, SteamFriends, CSteamID |
Vortex.Unity.EditorTools |
[ToggleButton] для Inspector |
Sirenix.OdinInspector |
[OnValueChanged] для Inspector |
Архитектура
SteamBus (static)
├── SteamEnabled: const bool ← #if USING_STEAM: true, иначе false
├── IsInitialized: bool ← SteamAPI.Init() прошёл успешно
├── IsLoaded: bool ← данные пользователя загружены
├── User: SteamUserData ← профиль + друзья
├── OnCallServices: Action ← сигнал для подписчиков загрузить свои данные
└── OnLoaded: Action
SteamManager : MonoBehaviour (singleton)
├── [RuntimeInitializeOnLoadMethod] Init()
│ └── new GameObject("SteamManager").AddComponent<SteamManager>()
├── Awake()
│ ├── Packsize.Test() / DllCheck.Test() → Debug.LogError при несовпадении версий
│ ├── RestartAppIfNecessary(AppId) → Application.Quit() если не через Steam
│ ├── SteamAPI.Init() → m_bInitialized
│ ├── Callback<UserStatsReceived_t>.Create()
│ ├── SteamBus.IsInitialized = true
│ └── LoadStatsInBus()
│ ├── SteamBus.LoadServices() → OnCallServices
│ ├── SteamBus.User.Init(steamId)
│ └── SteamBus.IsLoaded = true
├── Update() → SteamAPI.RunCallbacks()
└── OnDestroy() → SteamAPI.Shutdown()
SteamUserData : SteamUserShortData
├── SteamID: CSteamID
├── Name: string
├── Friends: Dictionary<CSteamID, SteamUserShortData>
├── OnUpdated: Action
└── Init(steamId) → загрузка профиля и списка друзей
SteamUserShortData
├── SteamID: CSteamID
├── Name: string
└── GetShortData(steamId) → factory
SteamConnectionSettings : ScriptableObject
├── steamAppId: uint (default 480)
├── isEnabled: bool ← [OnValueChanged("OnSteamEnabledChanged")] → DefineSymbolManager.Refresh()
├── isTestBuild: bool ← пропускает RestartAppIfNecessary
└── OnAppUdChanged() → File.WriteAllText("steam_appid.txt")
DefineSymbolManager (Editor-only)
├── [InitializeOnLoadMethod] Refresh()
└── ApplyDefineSymbol(bool, "USING_STEAM") → PlayerSettings
Settings (internal)
├── GetSettings() → Resources.LoadAll / CreateAsset
└── [InitializeOnLoadMethod] CheckSteamAppId() → синхронизация steam_appid.txt
Порядок инициализации
DefineSymbolManager.Refresh()— при загрузке Editor устанавливает/удаляетUSING_STEAMSettings.CheckSteamAppId()— синхронизируетsteam_appid.txtс ассетом настроекSteamManager.Init()—RuntimeInitializeOnLoadMethod, создаёт GameObjectSteamManager.Awake()—Packsize.Test()/DllCheck.Test(), затемRestartAppIfNecessary,SteamAPI.Init(), загрузка данных пользователяSteamBus.LoadServices()—OnCallServicesдля подписчиков (например,AchievementsController)
Условная компиляция
Сборка ru.vortex.steam.connection не имеет defineConstraints — компилируется всегда. Весь Steamworks-код внутри #if USING_STEAM. Без символа SteamBus.SteamEnabled = false, IsInitialized не может стать true. Это позволяет другим сборкам ссылаться на SteamBus без зависимости от Steamworks.
Безопасный режим без Steam
При isTestBuild = true пропускается RestartAppIfNecessary() — приложение не перезапускается через Steam-клиент. Используется для отладки из редактора.
Контракт
Вход
SteamConnectionSettingsвResources/Editor/—steamAppId,isEnabled,isTestBuild- Steamworks.NET подключён к проекту
- Steam-клиент запущен на машине
Выход
SteamBus.IsInitialized— Steam API инициализированSteamBus.IsLoaded— данные пользователя загруженыSteamBus.User— профиль, имя, список друзейSteamBus.OnCallServices— момент для загрузки зависимых сервисов
API
| Метод / Свойство | Описание |
|---|---|
SteamBus.SteamEnabled |
const bool — Steam подключён на уровне компиляции |
SteamBus.IsInitialized |
SteamAPI.Init() прошёл успешно |
SteamBus.IsLoaded |
Данные пользователя загружены |
SteamBus.User |
SteamUserData — текущий пользователь |
SteamBus.User.Friends |
Dictionary<CSteamID, SteamUserShortData> |
SteamBus.User.OnUpdated |
Событие при обновлении статистики (UserStatsReceived_t) |
SteamBus.OnCallServices |
Событие для инициализации зависимых сервисов |
Ограничения
| Ограничение | Причина |
|---|---|
| Требует запущенный Steam-клиент | SteamAPI.Init() возвращает false без клиента |
steam_appid.txt должен совпадать с настройками |
Автоматическая синхронизация при загрузке Editor |
DontDestroyOnLoad на SteamManager |
Единственный экземпляр на весь жизненный цикл |
Steamworks-вызовы запрещены в OnDestroy |
SteamAPI.Shutdown() уже мог быть вызван |
| Платформы: Windows, Linux, macOS | DefineSymbolManager фильтрует по standalone-платформам |
Использование
Настройка проекта
- Подключите Steamworks.NET к проекту
- Откройте
SteamConnectionSettingsвResources/Editor/(создаётся автоматически) - Укажите
steamAppIdвашего приложения - Включите
isEnabled— символUSING_STEAMдобавится автоматически - Для отладки без Steam-клиента включите
isTestBuild
Чтение данных пользователя
if (SteamBus.IsLoaded)
{
var name = SteamBus.User.Name;
var friends = SteamBus.User.Friends;
}
Подписка на загрузку сервисов
// Подключение к моменту инициализации Steam
SteamBus.OnCallServices += LoadMyService;
// Подписка на обновление статистики
SteamBus.User.OnUpdated += OnStatsReceived;
Условная компиляция в игровом коде
#if USING_STEAM
SteamBus.User.UnlockAchievement("ACH_001");
#endif
// Или через const:
if (SteamBus.SteamEnabled)
DoSteamStuff();
Граничные случаи
| Ситуация | Поведение |
|---|---|
| Steam-клиент не запущен | SteamAPI.Init() → false, IsInitialized остаётся false |
steam_api.dll не найден |
DllNotFoundException, Application.Quit() |
isTestBuild = false, запуск не через Steam |
RestartAppIfNecessary() → true, перезапуск через Steam-клиент |
isTestBuild = true |
RestartAppIfNecessary() пропускается |
Дубликат экземпляра SteamManager (s_instance != null) |
Destroy(gameObject) без лога |
Повторная инициализация при SteamBus.IsInitialized |
Debug.LogError, return (без Destroy) |
USING_STEAM не определён |
SteamBus.SteamEnabled = false, IsInitialized всегда false |
| Дублирующий друг в списке | TryAdd → Debug.LogError("There is broken data") |
| Domain Reload отключён | InitOnPlayMode() сбрасывает статические поля |
steamAppId = 0 |
Автоматически заменяется на 480 при загрузке Editor |