Repository Wiki
BeyondDimension/SteamTools

Steam 客户端集成与账号管理

本文档介绍 Watt Toolkit(SteamTools)客户端中 Steam 平台集成层的组织方式:ISteamService 抽象在 DI 容器中的注册、平台门控条件、SteamServiceImpl2 实现的装配位置,以及 SteamConnectService 等客户端侧 Steam 相关服务之间的关系。账号管理(登录账号读取、切换等)由该服务层对外提供统一入口。

Purpose and Scope

本页覆盖:

  • Steam 服务在客户端应用中的依赖注入(DI)装配方式:AddSteamService2() 扩展方法与平台条件编译门控
  • ISteamService / SteamServiceImpl2 的注册关系与所在代码层次
  • SteamConnectService 等 Steam 相关客户端服务的定位
  • 与 Steam 客户端集成相关的边界条件(平台不支持时的注册行为)

留给兄弟页面(不在本页展开):

  • ArchiSteamFarm 挂卡服务的完整实现(IArchiSteamFarmService、ArchiSteamFarmServiceImpl 等)属于 ASF 插件专题页
  • Steam 云存档、令牌(令牌管理器)等独立功能专题
  • 底层 BD.SteamClient 程序集内部的 Web API/截图/成就等细分服务

说明:本次文档生成受源码读取预算限制,仅完整核验了 DI 注册文件 ServiceCollectionExtensions.AddSteamService.cs 与目录结构。凡未能核验的实现细节,文中均明确标注"未核验",不做臆测。

Overview

Watt Toolkit 是一个多平台(Windows/macOS/Linux 桌面端,以及 iOS/Android 移动端)的 Steam 工具箱。Steam 客户端集成是整个应用的核心能力之一:它负责与本地 Steam 安装交互(例如读取 loginusers 等账号数据、管理 Steam 进程、连接测试等),并为上层 UI(账号切换、加速、挂卡等插件)提供统一的服务接口。

这一能力在代码中被拆分为三个层次:

  1. 抽象层:BD.SteamClient.Services.Implementation 命名空间承载的 ISteamService 接口(位于独立的 BD.SteamClient 代码层),定义了与 Steam 客户端集成相关的服务契约。
  2. 实现层:客户端应用 src/BD.WTTS.Client 中的 SteamServiceImpl2(路径 Services.Implementation/Steam/SteamServiceImpl2.cs),实现该接口。
  3. 装配层:ServiceCollectionExtensions.AddSteamService()(文件名)中的 AddSteamService2() 扩展方法,在应用启动时把实现注册为 DI 单例。

之所以用扩展方法 + 条件编译的方式装配,是因为该服务依赖桌面端特有的本地文件系统与进程交互能力;在移动平台上(iOS/Android)这段注册会被编译器整体裁剪掉,从而避免在上层代码中散落 if (platform) 判断。

Architecture

Loading diagram...

架构要点(均可在源码中核验):

  • AddSteamService2() 是唯一核验过的装配入口,它通过 services.AddSingleton<ISteamService, SteamServiceImpl2>() 将实现绑定到接口,并声明为单例——Steam 集成状态(如账号数据缓存)在整个应用生命周期内共享。
  • 整个注册方法被 #if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID) 包裹,即"桌面系平台且非移动端"才存在该注册。
  • SteamConnectService 位于 src/BD.WTTS.Client/Services/Mvvm/Steam/,属于 MVVM 层的 Steam 连接相关服务,与 ISteamService 处于不同分层。
  • ASF 相关服务(IArchiSteamFarmService 等)在独立插件目录 src/BD.WTTS.Client.Plugins.ArchiSteamFarmPlus/ 下,有自己的 AddArchiSteamFarmService 注册扩展,是兄弟专题,不在本页展开。

注册实现细节

以下是本页核验的核心源码——Steam 服务唯一的 DI 装配代码:

csharp
1// ReSharper disable once CheckNamespace 2using BD.SteamClient.Services.Implementation; 3 4namespace Microsoft.Extensions.DependencyInjection; 5 6public static partial class ServiceCollectionExtensions 7{ 8#if (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID) 9 /// <summary> 10 /// 添加 Steam 相关助手、工具类服务 11 /// </summary> 12 /// <param name="services"></param> 13 /// <returns></returns> 14 [MethodImpl(MethodImplOptions.AggressiveInlining)] 15 public static IServiceCollection AddSteamService2(this IServiceCollection services) 16 { 17 services.AddSingleton<ISteamService, SteamServiceImpl2>(); 18 return services; 19 } 20#endif 21}

Source: ServiceCollectionExtensions.AddSteamService.cs

逐点解读:

  1. 命名空间技巧:文件顶部的 // ReSharper disable once CheckNamespace 与 namespace Microsoft.Extensions.DependencyInjection 说明作者刻意将扩展方法挂在微软的 DI 命名空间下,使调用方无需额外 using 即可链式调用 services.AddSteamService2()。这是该项目(partial class ServiceCollectionExtensions)对所有功能装配的一贯风格。
  2. 实现类型来自外部程序集:using BD.SteamClient.Services.Implementation; 表明接口契约与(部分)实现位于 BD.SteamClient 代码层,客户端应用在此完成绑定。SteamServiceImpl2 本身位于客户端项目内 Services.Implementation/Steam/ 目录(未核验其内部实现)。
  3. AggressiveInlining:注册方法标记为激进内联,属于纯装配代码的微优化,避免在大量 Add* 调用中引入调用开销。
  4. "2" 后缀的含义:AddSteamService2 / SteamServiceImpl2 的命名暗示存在第一代实现(推测位于 BD.SteamClient 抽象层内),客户端应用用第二版实现替换了默认绑定。此点未核验原始 AddSteamService(不带 2)的注册,仅作命名层面的观察。

核心流程:应用启动时的装配时序

Loading diagram...

流程解释:装配发生在编译期裁剪之后、容器构建之前。运行期任何对 ISteamService 的解析都指向同一个 SteamServiceImpl2 单例,因此该实现内部若持有账号/连接状态,属于全局共享状态,需要注意线程安全(见下文边界条件)。

已核验的代码结构

通过文件清单可确认与 Steam 客户端集成相关的项目结构如下:

路径角色
src/BD.WTTS.Client/Extensions/Steam/ServiceCollectionExtensions.AddSteamService.csDI 装配入口(本页核心,已完整核验)
src/BD.WTTS.Client/Services.Implementation/Steam/SteamServiceImpl2.csISteamService 的客户端实现(未核验内部实现)
src/BD.WTTS.Client/Services/Mvvm/Steam/SteamConnectService.csMVVM 层 Steam 连接相关服务(未核验内部实现)
src/BD.WTTS.Client.Plugins.ArchiSteamFarmPlus/**ASF 挂卡插件,含独立的 ServiceCollectionExtensions.AddArchiSteamFarmService、IArchiSteamFarmService、IArchiSteamFarmWebApiService 及其实现(兄弟专题)

设计意图:主项目 BD.WTTS.Client 只保留与 Steam 客户端本地集成强相关的最小集合,而把挂卡等增值能力剥离到 Plugins.* 目录的独立程序集,各自携带自己的 ServiceCollectionExtensions 注册文件。这种"一个功能域 + 一个注册扩展文件"的模式让功能可以按插件粒度增删。

API Reference

AddSteamService2(services: IServiceCollection): IServiceCollection

用途:在客户端应用启动时注册 Steam 客户端集成服务(ISteamService → SteamServiceImpl2,单例)。

参数:

  • services (IServiceCollection):待追加注册的服务集合

返回值: 同一 IServiceCollection 实例,支持链式调用

可用性(编译期门控): 仅当 (WINDOWS || MACCATALYST || MACOS || LINUX) && !(IOS || ANDROID) 时该方法存在;iOS/Android 构建中该扩展方法不可用,编译期即被裁剪。

注意: ISteamService 接口本身的成员清单未在本次核验范围内(位于 BD.SteamClient 代码层),不在本页臆测其方法签名。

Failure Modes, Edge Cases & Concurrency

以下结论均直接来自已核验源码,未核验部分明确标注:

  • 平台不支持时:AddSteamService2 在移动端构建中不存在。若上层代码在这些平台上尝试以编译期直接调用的方式注册会直接编译失败——这是刻意的失败模式(fail-fast at compile time),而不是运行时抛 PlatformNotSupportedException。任何在共享代码中引用该方法的地方必须同样被平台条件包裹。
  • 单例生命周期:AddSingleton 意味着 SteamServiceImpl2 若持有可变状态(例如缓存的账号列表、连接探测结果),在多线程 UI/后台访问下需自行保证一致性。SteamServiceImpl2 内部是否实现锁或不可变快照未核验。
  • 命名冲突:类与方法名中的 2 后缀表明可能与旧版注册(AddSteamService)并存。若两个扩展都被调用,后注册者(在 TryAdd 缺失的情况下)以最后注册为准解析。本文件使用的是普通 AddSingleton(非 TryAddSingleton),因此后注册会覆盖先注册的解析结果。这一顺序敏感性是维护者需要注意的边界条件。

Extension Points

  • 替换实现:由于解析遵循"最后注册优先",测试或定制场景可在 AddSteamService2() 之后再 AddSingleton<ISteamService, TestDouble>() 以覆盖默认实现,无需修改源码。
  • 新增 Steam 能力:应遵循项目既有模式——在 BD.SteamClient 抽象层定义接口,在客户端实现,再在 Extensions/Steam/ 下新建(或扩展)ServiceCollectionExtensions 注册文件。
  • 插件化:与 Steam 相关但独立成体系的功能(如挂卡)应放入 src/BD.WTTS.Client.Plugins.* 独立目录并自带注册扩展,参照 ArchiSteamFarmPlus 的结构。