迅游 SDK 第三方加速集成
迅游(XunYou)加速器 SDK 集成是 Watt Toolkit(SteamTools)加速器插件的第三方加速通道之一。它在仓库中以 src/XunYouSDK 下的静态分部类 XunYouSDK 作为原生 SDK 的托管封装,并由加速器插件(BD.WTTS.Client.Plugins.Accelerator)在启动与各加速操作入口处接入。当前该集成处于占位/未激活状态:合作凭据常量为空,原生库加载逻辑被注释,IsSupported 恒为 false,所有迅游分支被安全旁路。
目的与范围
本页覆盖迅游 SDK 第三方加速集成的完整实现边界:
- SDK 封装层:
src/XunYouSDK/XunYouSDK.cs、XunYouSDK.AppId.cs、XunYouSDK.Constants.cs三个分部文件组成的static partial class XunYouSDK - 平台与架构探测:静态构造函数中对 OS 架构/进程架构的判定,以及
libraryPath的计算 - 合作凭据与签名存根:
appId、userType、channel_no、webapi_host等由迅游方分配的常量,以及CalcWebApiSign签名函数 - 集成点:加速器插件的
Plugin.cs初始化调用,以及BackendAcceleratorServiceImpl.XunYou.cs中各加速操作前的IsSupported守卫
以下内容有意留给兄弟页面,不在本页展开:
- 加速器插件整体的服务注册、UI 与加速流程编排——见"加速器插件"相关页面
BackendAcceleratorServiceImpl的主流程与其它第三方加速通道(其它 partial 实现文件)——见对应加速通道的页面- 原生库 P/Invoke 的通用基础设施——本页仅涉及迅游专属的
DllImportResolver设计意图(且当前为注释状态)
概述
迅游是国内的商业游戏加速服务商。Watt Toolkit 的加速器插件采用"多后端加速通道"设计,允许在不同运行环境下选择不同的第三方加速实现。迅游通道的结构是:
- 封装层(
src/XunYouSDK):一个static partial class XunYouSDK,以三个物理文件拆分职责——主文件负责平台检测与初始化,AppId.cs负责合作凭据与 WebApi 签名,Constants.cs负责原生库文件名常量。 - 原生层(设计目标,未激活):迅游提供的本机库
xunyoucall.dll(x86)与xunyoucall64.dll(x64),随程序集部署在其输出目录旁;以及迅游 WebApi(签名请求)。 - 接入层:加速器插件在
#if WINDOWS下调用XunYouSDK.Initialize();BackendAcceleratorServiceImpl的迅游分部实现(BackendAcceleratorServiceImpl.XunYou.cs)在每个迅游专属操作入口处先检查XunYouSDK.IsSupported,不支持时直接返回。
关键设计意图:整个迅游通道由一个单点开关 appId 控制——只有当迅游方分配了真实的合作 id(appId != 0)后,静态构造函数才会把 IsSupported 置为 true。这种"凭据即开关"的设计使得未签约的社区构建可以完整编译运行,而无需删除任何代码路径,同时保证所有迅游分支在未激活时零开销旁路。
架构
上图反映了集成的分层与单向依赖:
- 插件层 → SDK 层:插件只依赖
XunYouSDK的两个公共面——Initialize()与IsSupported。迅游分部实现不直接触碰原生库或凭据常量,而是完全经由 SDK 判定结果决定是否执行。 - SDK 层 → 原生层(虚线):当前全部是"设计目标"关系——
libraryPath只是被计算出来而从未加载(加载逻辑被注释),CalcWebApiSign返回空串,webapi_host为空。虚线表示这些链路尚未真正建立。 - 编译期裁剪:主文件的平台检测与初始化逻辑包裹在
#if WINDOWS中,非 Windows 平台编译产物中只剩IsSupported(恒为默认值false),保证跨平台构建无原生依赖。
分部类职责划分
三个物理文件在编译期合并为同一个 XunYouSDK 类型。这种拆分让"合作凭据"(AppId.cs)可以独立于"平台检测逻辑"(XunYouSDK.cs)维护——签约落地时只需替换凭据文件,无需触碰检测与初始化代码。
实现详解
平台检测与静态构造
XunYouSDK 的可用性判定发生在类型初始化(静态构造函数)阶段,而不是每次调用时动态判断。核心逻辑位于 XunYouSDK.cs 的 #if WINDOWS 块中:
1#if WINDOWS
2 static XunYouSDK()
3 {
4 if (appId != 0)
5 {
6 switch (RuntimeInformation.OSArchitecture)
7 {
8 case Architecture.X86:
9 case Architecture.X64:
10 IsSupported = true;
11 switch (RuntimeInformation.ProcessArchitecture)
12 {
13 case Architecture.X86:
14 libraryPath = Path.GetFullPath(Path.Combine(typeof(XunYouSDK).Assembly.Location, "..", libraryFileNameX86));
15 break;
16 case Architecture.X64:
17 libraryPath = Path.GetFullPath(Path.Combine(typeof(XunYouSDK).Assembly.Location, "..", libraryFileNameX64));
18 break;
19 }
20 break;
21 }
22 }
23 }
24#endifSource: XunYouSDK.cs
逐层拆解这段代码的判定链:
- 第一层:凭据门(
appId != 0)——这是整个迅游通道的总开关。appId是迅游方分配的合作 id,在XunYouSDK.AppId.cs中声明为const int appId = 0。当前仓库中它为0,因此整个静态构造函数体不会执行,IsSupported保持字段默认值false。 - 第二层:OS 架构(
RuntimeInformation.OSArchitecture)——只有操作系统本身是 x86 或 x64 才继续。迅游原生库不提供 ARM64 等其它架构,Windows on ARM(x64 模拟除外)会被排除。 - 第三层:进程架构(
RuntimeInformation.ProcessArchitecture)——注意这与第二层是两个不同维度:第二层决定"这个操作系统上能不能跑迅游库",第三层决定"当前进程应该加载哪一份 DLL"。例如在 x64 Windows 上运行 x86 进程时,libraryPath会指向xunyoucall.dll而非xunyoucall64.dll。libraryPath使用Path.Combine(Assembly.Location, "..", ...)解析为绝对路径,即原生库与 SDK 程序集同目录部署。
设计意图:把可用性判定全部前置到静态构造函数,使得 IsSupported 成为廉价的、线程安全的(static 字段初始化由 CLR 保证单次执行)、且在进程生命周期内不变的事实。调用方(BackendAcceleratorServiceImpl.XunYou.cs)因此可以在每个操作入口做无锁、无 try/catch 的守卫检查。
凭据常量与 WebApi 签名存根
合作凭据集中在 XunYouSDK.AppId.cs 中,全部为 const(编译期常量):
1static partial class XunYouSDK
2{
3 /// <summary>
4 /// 合作 id,由迅游给出明确值
5 /// </summary>
6 const int appId = 0;
7
8 /// <summary>
9 /// 账号类型,由迅游给出明确值
10 /// </summary>
11 const int userType = 0;
12
13 /// <summary>
14 /// 渠道版本,由迅游给出明确值
15 /// </summary>
16 const string channel_no = "";
17
18 const string webapi_host = "";
19
20 const string webapi_vip_endtime = "";
21
22 static string CalcWebApiSign(XunYouBaseRequest body)
23 {
24 return "";
25 }
26}Source: XunYouSDK.AppId.cs
各常量含义:
| 常量 | 类型 | 当前值 | 说明 |
|---|---|---|---|
appId | int | 0 | 迅游分配的合作 id,兼作整个迅游通道的启用开关 |
userType | int | 0 | 迅游分配的账号类型 |
string channel_no | string | "" | 渠道版本号 |
webapi_host | string | "" | 迅游 WebApi 服务主机地址 |
webapi_vip_endtime | string | "" | WebApi 会员到期时间相关参数 |
CalcWebApiSign(XunYouBaseRequest body) 是 WebApi 请求签名函数,当前实现直接返回空字符串。它的参数类型 XunYouBaseRequest 在本仓库中未找到定义(仅在此方法签名中被引用),说明该类型属于迅游 SDK 的原始分发内容或外部依赖,仓库内只保留了调用形态。
原生库常量与文件名约定
1static partial class XunYouSDK
2{
3 const string libraryName = libraryFileNameX64;
4
5 //const string libraryName = "xunyoucall";
6 public const string libraryFileNameX86 = "xunyoucall.dll";
7 public const string libraryFileNameX64 = "xunyoucall64.dll";
8}Source: XunYouSDK.Constants.cs
libraryFileNameX86/libraryFileNameX64是仅有的两个public常量,对应迅游 SDK 的两份原生库文件名。构建时它们需与XunYouSDK程序集输出到同一目录,静态构造函数中的Path.Combine(..., "..", ...)依赖这一部署约定。libraryName当前被定义为libraryFileNameX64,其旁有一行注释掉的//const string libraryName = "xunyoucall";,表明该常量曾经用于按名称引用原生库的 P/Invoke 场景,后改为直接引用 DLL 文件名常量。libraryName在当前代码中未被实际使用(仅出现在被注释掉的DllImportResolver代码中)。
初始化入口:Initialize() 与被注释的加载方案
Initialize() 是插件启动时调用的公共入口,但其当前实现为空方法(仅保留注释掉的历史实现):
1/// <summary>
2/// 初始化 <see cref="XunYouSDK"/>
3/// </summary>
4public static void Initialize()
5{
6 //if (isInitialize)
7 // return;
8 //isInitialize = true;
9 ...
10}Source: XunYouSDK.cs
被注释的代码揭示了一条清晰的历史演进轨迹,即"如何把原生库接进 .NET"的完整设计思路:
- 防重入:
isInitialize标志保证初始化只执行一次。 - 显式加载:
NativeLibrary.Load(libraryPath)把原生库显式加载进进程,失败时静默捕获(catch 空块)。 - 解析器重定向:
NativeLibrary.SetDllImportResolver(typeof(XunYouSDK).Assembly, DllImportResolver)为整个程序集注册自定义 DLL 解析器。其配套的DllImportResolver(同样被注释)在收到libraryName的解析请求时,若已持有libraryIntPtr则直接返回该句柄。
为什么要这样设计(设计意图):.NET 默认的 P/Invoke 解析按默认搜索顺序找 DLL,而迅游库需要按进程架构精确选择 x86/x64 两个文件,且必须从程序集旁目录加载。SetDllImportResolver + 预加载句柄的方案把"选哪份 DLL、从哪加载"这个决策完全收拢到 SDK 内部,对上层 P/Invoke 声明透明。这条链路当前整段被注释停用,libraryIntPtr 字段(//static nint libraryIntPtr;)也一并注释,与 appId = 0 的总开关状态一致——未签约前不需要真实加载原生库。
插件接入点
加速器插件的入口在 Plugin.cs 中,于 Windows 平台编译时执行一次初始化:
#if WINDOWS
XunYouSDK.Initialize();
#endifSource: Plugin.cs
迅游分部实现 BackendAcceleratorServiceImpl.XunYou.cs 则在每个迅游专属操作入口处重复同一守卫模式(该文件中至少 10 处):
1if (!XunYouSDK.IsSupported)
2{
3 ...
4}这一模式出现在 L83、L96、L117、L148、L166、L179、L212、L225、L238、L251 等多处,覆盖迅游通道的全部操作入口。所有守卫在 IsSupported == false 时提前返回,确保未激活的迅游通道不会产生任何原生调用或网络请求。
核心流程
下图展示从插件启动到一次迅游加速操作判定的完整链路(以当前未激活状态为准):
流程要点:
- 启动期:插件加载时调用一次
Initialize()。由于静态构造函数先于任何成员访问执行,即使Initialize()为空,IsSupported也已在首次触碰类型时完成判定。 - 运行期:每个迅游操作入口的守卫模式保证——在
appId == 0的社区构建中,迅游分支是纯"死分支":不加载原生库、不发网络请求、无异常抛出。 - 激活后的预期路径(设计目标,未实现):
appId被填入真实值后,静态构造会把IsSupported置true并计算libraryPath;届时需恢复Initialize()中被注释的NativeLibrary.Load+SetDllImportResolver链路,P/Invoke 声明即可正常解析到正确架构的原生库。
使用示例
示例 1:插件启动时初始化(实际接入方式)
#if WINDOWS
XunYouSDK.Initialize();
#endifSource: Plugin.cs
加速器插件在 Windows 平台编译时于启动路径调用一次 Initialize()。#if WINDOWS 保证非 Windows 构建中此调用被完全裁剪,不会因缺少 Initialize 的 Windows 专属实现而编译失败(该方法本身定义在 XunYouSDK.cs 的 #if WINDOWS 块内)。
示例 2:操作入口的可用性守卫
1if (!XunYouSDK.IsSupported)
2{
3 ...
4}这是迅游分部实现中重复出现的标准守卫。它读取的是静态构造函数已判定好的 IsSupported 属性,无锁、无 I/O、无异常路径,因此可以在高频入口处廉价地反复检查。
API 参考
XunYouSDK.IsSupported
public static bool IsSupported { get; private set; }Source: XunYouSDK.cs
含义:当前运行环境是否支持使用迅游加速器。
取值逻辑:true 需同时满足三个条件——(1) 编译目标为 Windows(#if WINDOWS);(2) appId != 0;(3) RuntimeInformation.OSArchitecture 为 X86 或 X64。任一不满足则保持默认值 false。
可见性注意:属性的 setter 为 private,外部代码无法篡改判定结果;但请注意 static 字段语义在多 AppDomain/测试隔离场景下不会重置,IsSupported 在单个进程内是初始化后即固定的。
XunYouSDK.Initialize(): void
参数:无。
返回:void,无返回值。
异常:当前实现为空方法,不抛出任何异常。被注释的历史实现中,NativeLibrary.Load 失败会被空 catch 静默吞掉,IsSupported 会被置 false——即初始化失败被设计为"降级为不支持",而非向调用方抛错。
线程安全:静态构造函数保证 IsSupported 的初始化由 CLR 保证恰好执行一次且对所有线程可见。Initialize() 被注释的历史实现额外用 isInitialize 标志做了防重入,当前空实现天然幂等。
XunYouSDK.CalcWebApiSign(body: XunYouBaseRequest): string(私有)
参数:body(类型 XunYouBaseRequest,该类型在仓库内无定义)。
返回:签名串;当前实现返回 ""。
可见性:私有静态方法,仅为 WebApi 请求签名而预留。激活后需按迅游文档实现签名算法并填入 webapi_host。
常量参考
| 常量 | 类型 | 可见性 | 当前值 | 说明 |
|---|---|---|---|---|
appId | int | private const | 0 | 合作 id,迅游通道总开关 |
userType | int | private const | 0 | 账号类型 |
channel_no | string | const | "" | 渠道版本 |
webapi_host | string | const | "" | WebApi 主机地址 |
webapi_vip_endtime | string | const | "" | 会员到期时间参数 |
libraryName | string | const | = libraryFileNameX64 | 原生库逻辑名(未使用) |
libraryFileNameX86 | string | public const | "xunyoucall.dll" | x86 原生库文件名 |
libraryFileNameX64 | string | public const | "xunyoucall64.dll" | x64 原生库文件名 |
失败模式、边界情况与并发
| 场景 | 行为 | 源码依据 |
|---|---|---|
appId == 0(当前状态) | IsSupported == false,所有迅游操作守卫提前返回,零原生调用 | XunYouSDK.cs L71-L92 |
| 非 Windows 平台 | 平台检测代码整体被 #if WINDOWS 裁剪,IsSupported 恒为 false | XunYouSDK.cs L8, L70 |
| OS 为 ARM64 | switch (RuntimeInformation.OSArchitecture) 无匹配分支,IsSupported 保持 false | XunYouSDK.cs L75-L90 |
| x64 Windows 上的 x86 进程 | IsSupported 为 true,libraryPath 指向 xunyoucall.dll(x86 版本) | XunYouSDK.cs L80-L88 |
| 原生库加载失败(激活后) | 历史实现为空 catch 静默 + IsSupported = false,降级而非抛错 | XunYouSDK.cs L52-L61(注释代码) |
| 原生库未随程序集部署 | libraryPath 仅是计算出的路径,不校验文件存在;真实加载在激活后才会暴露缺失 | XunYouSDK.cs L83-L87 |
并发访问 IsSupported | CLR 静态初始化保证单次执行与可见性;属性只读无竞态 | XunYouSDK.cs L68 |
CalcWebApiSign 被调用 | 返回空串;若激活后未实现签名,WebApi 请求会因签名缺失而失败 | XunYouSDK.AppId.cs L32-L35 |
并发要点:迅游 SDK 的可用性判定完全集中在类型初始化阶段,进程内只执行一次。IsSupported 为只读属性,所有调用方读到的都是初始化后的稳定值,不需要任何同步原语。被注释的 libraryIntPtr(原生库句柄)若被激活,将成为跨线程共享资源,需注意 NativeLibrary.Load 句柄的释放语义与 DllImportResolver 返回句柄的生命周期管理——这也是当时选择"加载一次、缓存句柄、解析器复用"设计的动机之一。
边界情况:XunYouBaseRequest 类型在仓库中不存在定义,CalcWebApiSign 的签名在当前形态下无法被有意义的调用;这说明该文件保留的是迅游 SDK 原始分发的接口形态,激活时需连同请求模型一起补齐。
性能与运维要点
- 零开销旁路:当前
appId == 0状态下,迅游通道不加载原生库、不发起任何网络请求,守卫检查只是一次静态属性读取。对社区构建的运行时性能与启动时间无可观测影响。 - 原生库部署约定:
libraryFileNameX86/libraryFileNameX64必须与XunYouSDK程序集输出同目录(静态构造用Assembly.Location + ".."解析)。运维打包时若改变目录结构会破坏该约定,激活后表现为加载失败 → 静默降级。 - 构建产物注意:由于
libraryPath逻辑在#if WINDOWS内,非 Windows 构建不产生对 DLL 文件名的运行时依赖;但两个文件名常量是public const,任何引用它们的外部程序集都会在编译期内联其值。 - 激活成本:激活迅游通道需要(1) 替换
AppId.cs中的凭据常量;(2) 实现CalcWebApiSign与请求模型;(3) 恢复Initialize()中被注释的NativeLibrary.Load+SetDllImportResolver链路;(4) 在构建中引入两份原生库文件。四步彼此独立,可分阶段推进。
扩展点
- 凭据文件独立替换:
AppId.cs是纯粹的凭据/签名分部文件,与平台检测逻辑完全解耦。签约落地时只需替换此文件,无需触碰XunYouSDK.cs的检测与初始化代码——这是分部类拆分的直接收益。 - DllImportResolver 自定义解析钩子:
SetDllImportResolver(typeof(XunYouSDK).Assembly, DllImportResolver)以程序集为粒度注册解析器,激活后可在不修改任何 P/Invoke 声明的前提下,统一控制该程序集内所有原生导入的加载来源(按进程架构选 DLL、从指定目录加载)。 - 新增第三方加速通道:迅游通道展示了"SDK 封装(
src/XunYouSDK)+ 插件守卫(IsSupported)+ 分部服务实现(BackendAcceleratorServiceImpl.XunYou.cs)"的三段式接入模式,可作为接入其它加速服务商的模板。 public const文件名常量:libraryFileNameX86/libraryFileNameX64暴露为公共常量,允许构建脚本或打包工具引用它们以校验原生库是否随产物部署。
相关链接
- src/XunYouSDK/XunYouSDK.cs — SDK 主文件:平台检测、
Initialize()、IsSupported - src/XunYouSDK/XunYouSDK.AppId.cs — 合作凭据与 WebApi 签名存根
- src/XunYouSDK/XunYouSDK.Constants.cs — 原生库文件名常量
- src/BD.WTTS.Client.Plugins.Accelerator/Plugins/Plugin.cs — 插件启动初始化入口
- src/BD.WTTS.Client.Plugins.Accelerator/Services.Implementation/BackendAcceleratorServiceImpl.XunYou.cs — 迅游加速服务分部实现(守卫模式)