Files

BD2.GameSdk

本插件不会提供任何封装的游戏 API,任何功能的 patch 等操作需要自行处理。

给 Brown Dust II 插件作者使用的 C# 开发包。通过标准 NuGet PackageReference 导入,自动完成 生成可读引用程序集和全量源码导航 → 编译插件 → reobf → 验证运行 DLL。源码可以使用可读类型和成员,字符串反射由配套的 BD2.GameNames 处理。转到定义可查看完整反编译方法体。

本发布包

NuGet 包 内容 使用方式
BD2.GameSdk .NET 8 构建工具、build/*.props/targets、压缩名字表 插件项目直接引用,PrivateAssets="all"
BD2.GameNames lib/netstandard2.0/BD2.GameNames.dll、XML API 文档、同一份内嵌表 SDK 固定依赖对应版本,自动引入;DLL 随插件部署

升级游戏后更新此包并重新构建插件;相同游戏版本下的不同官方 DLL 也会因指纹不同而被拒绝。

包不包含游戏 DLL 或可读壳。用官方映射生成名字表,提交为 plugins/GameNames/Mappings/names.json.gz,SDK 和运行时从同一文件嵌入,插件作者只需要对应游戏客户端和 BepInEx。

包当前由维护者提供 .nupkg 或 NuGet 源,尚未发布到 nuget.org。包内包含仓库 LICENSE,许可条款沿用项目现有授权,不声明为开源许可。

导入到自己的插件项目

前提:安装 .NET 8 SDK,准备与包对应版本的游戏目录,并在该目录安装 BepInEx。推荐 SDK-style .csproj 与 netstandard2.1;运行时库本身兼容 .NET Standard 2.0。旧式 packages.config 不受支持。

将维护者提供的两个 .nupkg 放在一个目录,例如 D:\NuGet\BD2,添加为本地 NuGet 源:

dotnet nuget add source 'D:\NuGet\BD2' --name BD2
dotnet add MyPlugin.csproj package BD2.GameSdk --version 0.2.2-game.2.35.10

Visual Studio / Rider 也可在 NuGet 包管理界面添加该源。

简单的一个项目文件:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard2.1</TargetFramework>
    <LangVersion>latest</LangVersion>
    <AssemblyName>MyPlugin</AssemblyName>
    <BD2GameVersion>2.35.10</BD2GameVersion>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="BD2.GameSdk" Version="0.2.2-game.2.35.10" PrivateAssets="all" />
  </ItemGroup>
</Project>

PrivateAssets="all" 让构建步骤只用于当前插件项目。SDK 自动引入 BD2.GameNames,以及游戏目录中的 BepInEx、0Harmony、UnityEngine、UnityEngine.CoreModule 引用,无需手写 <Import>。不要再引用真实的 Assembly-CSharp.dll,游戏 API 编译引用由 SDK 提供。

在项目旁创建仅保存在本机的 Directory.Build.props,并加入自己的 .gitignore:

<Project>
  <PropertyGroup>
    <GameDir>E:\Games\BrownDustII</GameDir>
  </PropertyGroup>
</Project>

或者构建时传入目录:

dotnet build MyPlugin.csproj -c Release '-p:GameDir=E:\Games\BrownDustII'

IDE 项目加载时构建会准备可读引用,CLI 首次构建同样会自动生成。完整示例在 samples/ExamplePlugin。

完整源码导航

导入包、设置 GameDir 后,先完成一次构建。SDK 会使用 ILSpy 从匹配版本的真实游戏 DLL 生成全部类型的可读 C# 源码,保留方法体、私有成员、嵌套类型和编译器生成实现;同步生成 Portable PDB,将源码内嵌到 PDB。

在支持外部源码导航的 IDE 中,对 NetworkManager.Send 等可读 API 使用“转到定义”(例如 Visual Studio 的 F12),即可打开完整方法体。重载、泛型、参数、属性、字段、事件和嵌套类型使用真实元数据及 PDB 定位。

首次全量生成会花费数分钟,且体积稍微有些许庞大。本机 2.35.10 的一次验证生成了 24,856 个源码文件、654,265 个声明、319,350 个带方法体的符号。生成结果按名字表、生成器及其依赖、全部游戏 Managed DLL 的指纹缓存,同一台机器上的插件项目共用。

所有依赖 SDK 的插件必须在自己的 .csproj 明确声明目标游戏版本:

<BD2GameVersion>2.35.10</BD2GameVersion>

构建核对声明值、SDK 内嵌表、真实游戏 DLL 指纹。缺少声明或版本不匹配会报错;不会自动选用其他版本。NuGet 项目仍需安装带对应游戏版本的包。

仓库默认缓存根为 .build/game-sdk,与源码位于同一盘。第三方 NuGet 项目默认使用 %LOCALAPPDATA%\BD2\GameSdk\navigation;可在本机 Directory.Build.props 设置 BD2GameSdkCache,或设置环境变量 BD2_GAME_SDK_CACHE。 obj 不影响共享缓存。缺失的共享源码可在下次准备时从 PDB 恢复,只有删除共享缓存才会触发重新生成。

lib 中的程序集仅供开发导航,不要部署或执行。这里展示的是当前 DLL 的反编译源码,局部变量名和语法可能与原始工程不同。导航 PDB 对应可读程序集,不能用于真实混淆游戏 DLL 的逐行调试。

公开方法调用、字段和属性访问可以直接使用可读名字,获得 IDE 补全和编译检查。需要通过反射或 Harmony 定位成员时,已知游戏类型优先使用 typeof(GameType),避免用字符串查找类型,可访问的成员可以使用 nameof 提供经过编译检查的名字,或者通过强类型表达式取得 MethodInfo,让编译器检查所选重载及参数类型。表达式只用于取得方法信息,不执行方法。私有成员保留原访问性,通过已知类型和映射反射 API 查询,例如:

using System.Reflection;

var intro = typeof(IntroUI);
var enter = intro.GetGameMethod("Enter",
    BindingFlags.Instance | BindingFlags.NonPublic,
    null, Type.EmptyTypes, null);

var field = typeof(IntroUI).GetGameField("_maintenanceTimeoutCts",
    BindingFlags.Instance | BindingFlags.NonPublic);

// nameof 的结果仍是可读字符串,必须传给 GetGameMethod 等运行时 API。
var maintenance = typeof(IntroUI).GetGameMethod(nameof(IntroUI.SendMaintenanceInfo),
    BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(bool) }, null);

// 对实际协程可读名调用 MemberName,再交给 Unity 的字符串 API。
owner.StartCoroutine(Game.MemberName(owner.GetType(), "ReadableCoroutineName"));

GetGameMethod/GetGameField/GetGameProperty/GetGameEvent 采用 .NET 反射约定:查不到返回 null,重载歧义抛出 AmbiguousMatchException,null 名字参数抛出 ArgumentNullException。MemberName 的成员种类使用 GameMemberKind 枚举。未知映射保留字面名,普通 UI 文案、nameof 字符串和协程常量都不会被 reobf 自动替换。

Game.FindType 保留给运行时才知道类型名称的查询。只做字符串反射的项目可单独安装 BD2.GameNames,启动时用 Game.ValidateGame(...)。这种方式没有可读游戏类型引用,也没有编译指纹,使用 SDK 的项目必须用 Game.Validate(typeof(MyPlugin).Assembly, ...)。

构建与部署

构建成功后,将你的插件产物和相邻的 BD2.GameNames.dll 安装到游戏 BepInEx/plugins。当然,客户端只需要安装一个BD2.GameNames.dll,GameSdk.dll只是编写时需要。

MSBuild 属性 用途
BD2GameVersion 每个插件 csproj 必填的目标游戏版本,例如 2.35.10;不从 SDK 包版本或仓库版本自动推断
GameDir 游戏根目录,本机安装路径(另须在插件 csproj 声明 BD2GameVersion)
BD2ManagedDir 自定义 Managed 路径,默认 GameDir/BrownDust II_Data/Managed
BD2BepInExDir 自定义 BepInEx 路径,默认 GameDir/BepInEx
BD2GameSdkCache 共享导航缓存目录,可在 Directory.Build.props 配置;默认取 BD2_GAME_SDK_CACHE 环境变量或用户缓存目录
BD2AddBepInExReferences=false 使用其他宿主或自己提供 BepInEx/Harmony 引用时关闭自动引用
BD2AddUnityReferences=false 自己提供 Unity 引用时关闭自动引用
BD2GameSdkEnabled=false 暂时关闭壳生成与 reobf;只适用于不依赖可读游戏 API 的项目