Ducky.Sdk
0.1.8
dotnet add package Ducky.Sdk --version 0.1.8
NuGet\Install-Package Ducky.Sdk -Version 0.1.8
<PackageReference Include="Ducky.Sdk" Version="0.1.8"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
<PackageVersion Include="Ducky.Sdk" Version="0.1.8" />
<PackageReference Include="Ducky.Sdk"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets> </PackageReference>
paket add Ducky.Sdk --version 0.1.8
#r "nuget: Ducky.Sdk, 0.1.8"
#:package Ducky.Sdk@0.1.8
#addin nuget:?package=Ducky.Sdk&version=0.1.8
#tool nuget:?package=Ducky.Sdk&version=0.1.8
Ducky.Sdk
中文 | English
一个用于为"Escape from Duckov"游戏开发 Mod 的综合性 .NET SDK。
功能特性
- 🚀 自动化构建管道 - 构建时自动部署到游戏目录
- 🌍 智能本地化 - 基于源生成器的本地化系统,支持 CSV/文件翻译
- 📦 单 DLL 分发 - 通过 ILRepack 自动合并程序集,无冲突部署
- 🔧 Harmony 集成 - 可选的运行时补丁支持,无缝依赖管理
- 🎨 自动生成资源 - 自动生成 Mod 元数据和预览图
- 📝 强类型开发 - 完整的 IntelliSense 支持和编译时验证
- 🔄 源码分发 - SDK 以源代码形式分发,避免版本冲突
快速开始
前置要求
- .NET SDK 9.0 或更高版本
- 已安装"Escape from Duckov"游戏
- Steam 安装目录(用于自动部署)
安装
- 在项目根目录创建
Local.props(git 忽略):
<Project>
<PropertyGroup>
<SteamFolder>/path/to/your/steam/</SteamFolder>
</PropertyGroup>
</Project>
- 配置你的 Mod 项目:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<ModName>MyAwesomeMod</ModName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Ducky.Sdk" Version="*">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
</Project>
CI/CD 环境配置
在持续集成环境(如 GitHub Actions、Azure Pipelines)中,无需安装 Steam 即可构建 Mod。通过环境变量直接指定关键路径:
# GitHub Actions 示例
env:
ManagedDirectory: /opt/game-assemblies/Managed/
ModsDirectory: /tmp/mod-output/
或通过 MSBuild 参数传递:
dotnet build -p:ManagedDirectory=/path/to/managed/ -p:ModsDirectory=/path/to/mods/
CI 构建优势:
- 无需 Steam 安装
- 无需游戏文件
- 灵活的输出路径配置
- 自动跳过
SteamFolder验证(检测CI环境变量)
你的第一个 Mod
创建 ModBehaviour.cs 文件:
using Ducky.Sdk.Logging;
using Ducky.Sdk.ModBehaviours;
namespace MyAwesomeMod;
public class ModBehaviour : ModBehaviourBase
{
protected override void ModEnabled()
{
Log.Info("我的超棒 Mod 已启用!");
// 在这里编写初始化代码
}
protected override void ModDisabled()
{
Log.Info("我的超棒 Mod 已禁用!");
// 在这里编写清理代码
}
}
构建并运行:
dotnet build
# 你的 Mod 会自动部署到游戏目录!
包发布渠道
Ducky.Sdk 通过以下渠道发布:
NuGet.org (稳定版)
- 主要发布渠道 - 面向生产环境使用
- 官方稳定版本 - 经过完整测试的正式版本
- 访问地址: https://www.nuget.org/packages/Ducky.Sdk/
MyGet (预览版)
- 预览发布渠道 - 开发版本和预发布版本
- 快速分发 - 开发者可快速获取最新功能
- 持续集成 - main 分支的每次构建都会自动发布
- 访问地址: https://www.myget.org/feed/duckysdk/package/nuget/Ducky.Sdk
双重发布机制
- 容错设计: 即使其中一个发布渠道失败,另一个仍会继续发布
- 同步版本: 标签版本会同时发布到两个渠道
- 开发版本: main 分支的构建版本优先在 MyGet 上提供
推荐使用方式
- 生产环境: 使用 NuGet.org 稳定版本
- 开发测试: 使用 MyGet 预览版本以获取最新功能
- 体验测试: 在项目中指定预发布版本:
<PackageReference Include="Ducky.Sdk" Version="*" />
核心概念
ModBehaviour 模式
每个 Mod 都继承 ModBehaviourBase,它提供:
- 生命周期钩子:
ModEnabled()和ModDisabled() - 自动初始化:日志记录、本地化和 buff 注册
- Mod 身份:访问 Mod 名称、ID 和 Steam 创意工坊元数据
public class ModBehaviour : ModBehaviourBase
{
protected override void ModEnabled()
{
// 当 Mod 被加载时调用
// 初始化系统、注册事件、应用补丁
}
protected override void ModDisabled()
{
// 当 Mod 被卸载时调用
// 清理资源、移除补丁、注销事件
}
}
本地化系统
SDK 使用独特的双类模式实现类型安全的本地化:
1. 定义键(LK.cs)
using Ducky.Sdk.Attributes;
[LanguageSupport("en", "zh", "fr")]
public static class LK
{
public static class UI
{
public const string Welcome = "欢迎使用我的 Mod!";
public const string Settings = "设置";
[TranslateFile("md")]
public const string Documentation = "文档";
}
}
2. 使用生成的属性(自动生成的 L.cs)
using Ducky.Sdk.Localizations;
// SDK 会生成一个匹配的 L 类及其属性
Log.Info(L.UI.Welcome); // 返回本地化字符串
Log.Info(L.UI.Documentation); // 返回 Documentation.md 的内容
3. 管理翻译
翻译存储在 assets/Locales/:
assets/
Locales/
en.csv # 英文翻译
zh.csv # 中文翻译
en/
Documentation.md # 英文长文本内容
zh/
Documentation.md # 中文长文本内容
CSV 格式:
Key,Value
欢迎使用我的 Mod!,欢迎使用我的 Mod!
设置,设置
文档,Documentation.md
4. 多目录本地化支持
通过 LocalizationAssetsDir 属性,您可以指定多个本地化资源目录:
<PropertyGroup>
<LocalizationAssetsDir>assets/Locales;shared-locales;external-translations</LocalizationAssetsDir>
</PropertyGroup>
当指定多个目录时:
- 每个目录都应包含相同结构的 CSV 文件
UpdateLocalesCsvtarget 会为所有目录生成和验证翻译文件- 其他资源文件仍使用
AssetsDir指定的目录
配置管理
存储和检索 Mod 设置:
using Ducky.Sdk.Options;
// 每个 Mod 的配置(隔离存储)
ModOptions.ForThis.Set("volume", 0.8);
var volume = ModOptions.ForThis.Get("volume", 1.0);
// 共享配置(跨所有 Mod)
ModOptions.ForAllMods.Set("globalSetting", "value");
日志记录
使用 LibLog 进行结构化日志记录:
using Ducky.Sdk.Logging;
Log.Info("玩家加入:{PlayerName}", playerName);
Log.Warn("生命值过低:{Health}", health);
Log.Error(exception, "加载资源失败:{ResourceId}", resourceId);
高级功能
MessageHub 跨 Mod 通信
SDK 集成了 MessageHub 功能,允许 mod 之间进行通信,无需安装额外的 host mod。
自动 Host 功能
public class MyModBehaviour : ModBehaviourBase
{
// 默认启用,可以选择禁用
protected override bool EnableMessageHubHost { get; set; } = true;
protected override void ModEnabled()
{
// 检查此 mod 是否是 host
if (IsMessageHubHost)
{
Log.Info("此 Mod 是 MessageHub Host!");
}
// 注册为消息接收方
var proxy = ModHttpV1Proxy.CreateFromSingleton();
proxy.RegisterClient("MyMod", async (fromMod, contentType, body) =>
{
Log.Info($"收到来自 {fromMod} 的消息: {body}");
// 处理消息...
});
}
}
发送消息到其他 Mod
var proxy = ModHttpV1Proxy.CreateFromSingleton();
await proxy.Notify("MyMod", "TargetModName", "custom_event", "Hello World!");
特性
- ✅ 无需额外安装 - 自动集成在 SDK 中
- ✅ 自动 Host 检测 - 第一个启动的 mod 成为 host
- ✅ 向后兼容 - 与现有代码完全兼容
- ✅ 生命周期管理 - Host 会持续运行,即使创建它的 mod 已禁用
Harmony 运行时补丁
启用运行时方法补丁以实现高级 Mod:
<PropertyGroup>
<ModName>MyAwesomeMod</ModName>
<IncludeHarmony>true</IncludeHarmony>
</PropertyGroup>
使用 Harmony 补丁:
using HarmonyLib;
public class ModBehaviour : ModBehaviourBase
{
private Harmony _harmony;
protected override void ModEnabled()
{
_harmony = new Harmony("com.myname.mymod");
_harmony.PatchAll();
}
protected override void ModDisabled()
{
_harmony?.UnpatchAll();
}
}
[HarmonyPatch(typeof(Player), nameof(Player.TakeDamage))]
public static class Player_TakeDamage_Patch
{
static void Prefix(Player __instance, ref float damage)
{
Log.Info("玩家受到伤害:{Damage}", damage);
damage *= 0.5f; // 伤害减少 50%
}
}
程序集合并
默认情况下,SDK 会将所有依赖项合并到单个 DLL:
<PropertyGroup>
<EnableILRepack>true</EnableILRepack>
<EnableILRepack>false</EnableILRepack>
</PropertyGroup>
优点:
- ✅ 单文件分发
- ✅ Mod 之间无版本冲突
- ✅ 内部化依赖项(无命名空间污染)
- ✅ 更小的部署占用空间
多项目 Mod
将复杂的 Mod 组织成多个项目:
共享库项目(MyMod.Common.csproj):
<PropertyGroup>
<IsModLib>true</IsModLib>
<AssetsDir>$(SolutionDir)/MyMod/assets</AssetsDir>
</PropertyGroup>
入口项目(MyMod.csproj):
<PropertyGroup>
<ModName>MyMod</ModName>
<ExcludeSdkLib>true</ExcludeSdkLib>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../MyMod.Common/MyMod.Common.csproj" />
</ItemGroup>
IsModLib 行为差异
共享库项目(IsModLib=true)会跳过以下自动化任务:
- ❌ 预览图片(
preview.png)生成 - ❌ Mod 元数据(
info.ini)生成 - ❌ 自动部署到游戏目录
- ❌ ILRepack 程序集合并
- ❌ 依赖文件复制
保留的功能:
- ✅ 本地化处理(字符串提取和 CSV 更新)
- ✅ 编译和程序集生成
最终发布 Mod 项目(IsModLib=false 或未设置)会执行所有自动化任务。
项目属性说明
IsModLib=true: 标记项目为共享库,跳过非必要的自动化任务ExcludeSdkLib=true: 排除 SDK 源代码编译(用于入口项目,避免重复编译)
自动生成资源
SDK 会自动生成:
- info.ini - 基本 Mod 元数据(名称、显示名称、描述)
- preview.png - 基于 Mod 名称的 256x256 identicon
- publishedFileId - Steam 创意工坊 ID 同步
开发期间禁用自动部署:
<PropertyGroup>
<DeployMod>false</DeployMod>
</PropertyGroup>
项目结构
MyMod/
├── MyMod.csproj # 主 Mod 项目
├── ModBehaviour.cs # Mod 入口点
├── LK.cs # 本地化键
├── Local.props # Git 忽略的本地配置
└── assets/
├── info.ini # Mod 元数据
├── preview.png # Mod 图标
├── description.md # 详细描述
└── Locales/
├── en.csv # 英文翻译
├── zh.csv # 中文翻译
└── ...
SDK 开发
本地构建 SDK
- 打包到本地源:
./scripts/packToLocal.sh --version 0.0.1
- 使用新 SDK 重建示例:
./scripts/rebuild_samples.sh
- 获取游戏依赖:
./scripts/fetch_build_dependency.sh
测试更改
Samples/ 目录包含集成测试项目:
- Ducky.SingleProject - 单项目 Mod 模板
- Ducky.EntranceMod - 带共享库的多项目 Mod
- Ducky.TryHarmony - Harmony 补丁示例
运行 ./scripts/rebuild_samples.sh 验证端到端 SDK 工作流程。
仓库结构
Ducky.Sdk/
├── Sdk/ # SDK 开发工作区
│ ├── SDKlibs/
│ │ ├── Ducky.Sdk/ # 核心 NuGet 包
│ │ │ ├── Ducky.Sdk.nuspec # 包清单
│ │ │ ├── Ducky.Sdk.props # MSBuild 属性
│ │ │ ├── Ducky.Sdk.targets # 构建目标
│ │ │ └── scripts/*.csx # 自动化脚本
│ │ └── Ducky.Sdk.Lib/ # 共享库(以源码分发)
│ ├── Ducky.Sdk.Analyser/ # Roslyn 源生成器
│ └── Tests/ # 单元测试
├── Samples/ # 示例 Mod 项目
│ ├── Ducky.SingleProject/
│ ├── Ducky.EntranceMod/
│ └── Ducky.TryHarmony/
├── scripts/ # 构建自动化
│ ├── packToLocal.sh # 打包 SDK 到本地源
│ ├── rebuild_samples.sh # 使用新 SDK 重建示例
│ └── fetch_build_dependency.sh # 下载游戏程序集
└── duckylocal/ # 本地 NuGet 源
配置参考
MSBuild 属性
| 属性 | 默认值 | 描述 |
|---|---|---|
ModName |
(必需) | Mod 标识符和输出 DLL 名称 |
SteamFolder |
- | Steam 安装路径 |
DuckovFolder |
计算得出 | 游戏目录(从 SteamFolder 自动计算) |
ManagedDirectory |
计算得出 | 游戏托管程序集目录(可显式设置用于 CI 环境) |
ModsDirectory |
计算得出 | Mod 部署目标目录(可显式设置用于 CI 环境) |
DeployMod |
true |
启用自动部署到游戏 |
EnableILRepack |
true |
将程序集合并到单个 DLL |
IncludeHarmony |
false |
包含 Harmony 用于运行时补丁 |
AssetsDir |
assets/ |
自定义资源目录路径(用于一般资源) |
LocalizationAssetsDir |
$(AssetsDir) |
本地化资源目录路径(用于 UpdateLocalesCsv target) |
ExcludeSdkLib |
true |
排除 SDK 源代码编译(用于入口项目) |
IsModLib |
false |
将项目标记为共享库(跳过图片生成、部署、ILRepack 等自动化任务,仅保留本地化处理) |
AssetsDir vs LocalizationAssetsDir
AssetsDir 和 LocalizationAssetsDir 有不同的用途:
AssetsDir: 用于一般资源(如info.ini、preview.png等),期望为单一路径LocalizationAssetsDir: 专用于本地化资源,支持多个目录(用分号分隔)
使用场景:
<PropertyGroup>
<AssetsDir>assets</AssetsDir>
</PropertyGroup>
<PropertyGroup>
<AssetsDir>assets</AssetsDir>
<LocalizationAssetsDir>locales;shared-locales</LocalizationAssetsDir>
</PropertyGroup>
本地化属性
[LanguageSupport(...)]
指定支持的语言:
[LanguageSupport("en", "zh", "fr", "de", "ja")]
public static class LK { ... }
使用 "all" 生成所有支持语言的文件:
[LanguageSupport("all")]
public static class LK { ... }
使用 "all" 时支持的语言:de, en, es, fr, ja, ko, pt, ru, zh-hant, zh
混合使用 "all" 和额外语言:
[LanguageSupport("all", "custom-lang")]
public static class LK { ... }
[TranslateFile] 或 [TranslateFile("ext")]
在外部文件中存储翻译:
[TranslateFile] // 使用 .txt 扩展名
public const string Help = "帮助文本";
[TranslateFile("md")] // 使用 .md 扩展名
public const string ReadMe = "说明内容";
故障排除
"SteamDir property must be set"
本地开发: 在项目根目录创建 Local.props,填写 Steam 安装路径:
<Project>
<PropertyGroup>
<SteamFolder>/path/to/steam/</SteamFolder>
</PropertyGroup>
</Project>
CI 环境: 设置 ManagedDirectory 和 ModsDirectory 环境变量:
env:
ManagedDirectory: /path/to/managed/
ModsDirectory: /path/to/mods/
或通过 MSBuild 参数:
dotnet build -p:ManagedDirectory=/path/to/managed/ -p:ModsDirectory=/path/to/mods/
NuGet 缓存过期
清除所有缓存并重建:
./scripts/rebuild_samples.sh --clear-all-caches
或手动清除:
dotnet nuget locals all --clear
rm -rf ~/.nuget/packages/ducky.sdk/
缺少游戏程序集
下载所需的游戏 DLL:
./scripts/fetch_build_dependency.sh
Mod 未部署
- 验证
$(DuckovFolder)路径存在 - 检查
$(DeployMod)是否设置为false - 确保对游戏目录有写入权限
CSV 中缺少本地化键
SDK 会验证 CSV 文件包含所有键。运行:
dotnet build
检查构建输出中的验证错误。
示例
查看 Samples/ 目录获取完整示例:
- Ducky.SingleProject - 最小单文件 Mod
- Ducky.EntranceMod - 带本地化的多项目 Mod
- Ducky.TryHarmony - 使用 Harmony 的运行时补丁
贡献
欢迎贡献!请:
- Fork 仓库
- 创建功能分支
- 进行更改
- 使用
./scripts/rebuild_samples.sh测试 - 提交 Pull Request
许可证
本项目采用 MIT 许可证 - 详见 LICENSE 文件。
致谢
- 游戏:"Escape from Duckov" by TeamSoda
- Harmony:Harmony Library
- ILRepack:dotnet-ilrepack
支持
用 ❤️ 为 Escape from Duckov Mod 社区打造
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.1 is compatible. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
This package has no dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.1.8 | 101 | 1/6/2026 |
| 0.1.8-dev.1 | 47 | 1/6/2026 |
| 0.1.7 | 96 | 1/6/2026 |
| 0.1.7-dev.4 | 57 | 1/6/2026 |
| 0.1.7-dev.3 | 53 | 1/6/2026 |
| 0.1.7-dev.2 | 535 | 12/1/2025 |
| 0.1.7-dev.1 | 439 | 12/1/2025 |
| 0.1.6 | 194 | 11/27/2025 |
| 0.1.6-dev.21 | 141 | 11/27/2025 |
| 0.1.6-dev.20 | 145 | 11/27/2025 |
| 0.1.6-dev.19 | 152 | 11/27/2025 |
| 0.1.6-dev.18 | 146 | 11/27/2025 |
| 0.1.6-dev.17 | 159 | 11/26/2025 |
| 0.1.6-dev.16 | 149 | 11/25/2025 |
| 0.1.6-dev.15 | 145 | 11/25/2025 |
| 0.1.6-dev.14 | 145 | 11/25/2025 |
| 0.1.6-dev.13 | 145 | 11/25/2025 |
| 0.1.6-dev.12 | 151 | 11/25/2025 |
| 0.1.6-dev.11 | 152 | 11/25/2025 |
| 0.1.6-dev.10 | 148 | 11/25/2025 |
See https://github.com/ducky7go/Ducky.Sdk/releases for release notes.