Ducky.Sdk is a comprehensive .NET SDK for developing mods for the "Escape from Duckov" game. Features: 🚀 Automated Build Pipeline - Auto-deploy to game directory on build 🌍 Smart Localization - Source generator-based localization system with CSV/file translations 📦 Single DLL Distribution - Automatic assembly merging via ILRepack for conflict-free deployment 🔧 Harmony Integration - Optional runtime patching support with seamless dependency management 🎨 Auto-Generated Assets - Automatic generation of mod metadata and preview images 📝 Strongly-Typed Development - Full IntelliSense support and compile-time validation 🔄 Source Distribution - SDK distributed as source code to avoid version conflicts Perfect for modders who want a modern, type-safe development experience with automated workflows. --- Ducky.Sdk 是一个用于为"Escape from Duckov"游戏开发 Mod 的综合性 .NET SDK。 功能特性: 🚀 自动化构建管道 - 构建时自动部署到游戏目录 🌍 智能本地化 - 基于源生成器的本地化系统,支持 CSV/文件翻译 📦 单 DLL 分发 - 通过 ILRepack 自动合并程序集,无冲突部署 🔧 Harmony 集成 - 可选的运行时补丁支持,无缝依赖管理 🎨 自动生成资源 - 自动生成 Mod 元数据和预览图 📝 强类型开发 - 完整的 IntelliSense 支持和编译时验证 🔄 源码分发 - SDK 以源代码形式分发,避免版本冲突 为希望获得现代化、类型安全开发体验和自动化工作流的 Mod 开发者量身打造。
$ dotnet add package Ducky.Sdk中文 | English
一个用于为"Escape from Duckov"游戏开发 Mod 的综合性 .NET SDK。
Local.props(git 忽略):<Project>
<PropertyGroup>
<SteamFolder>/path/to/your/steam/</SteamFolder>
</PropertyGroup>
</Project>
<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>
在持续集成环境(如 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 构建优势:
SteamFolder 验证(检测 CI 环境变量)创建 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 通过以下渠道发布:
<PackageReference Include="Ducky.Sdk" Version="*" />每个 Mod 都继承 ModBehaviourBase,它提供:
ModEnabled() 和 ModDisabled()public class ModBehaviour : ModBehaviourBase
{
protected override void ModEnabled()
{
// 当 Mod 被加载时调用
// 初始化系统、注册事件、应用补丁
}
protected override void ModDisabled()
{
// 当 Mod 被卸载时调用
// 清理资源、移除补丁、注销事件
}
}
SDK 使用独特的双类模式实现类型安全的本地化:
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 = "文档";
}
}
using Ducky.Sdk.Localizations;
// SDK 会生成一个匹配的 L 类及其属性
Log.Info(L.UI.Welcome); // 返回本地化字符串
Log.Info(L.UI.Documentation); // 返回 Documentation.md 的内容
翻译存储在 assets/Locales/:
assets/
Locales/
en.csv # 英文翻译
zh.csv # 中文翻译
en/
Documentation.md # 英文长文本内容
zh/
Documentation.md # 中文长文本内容
CSV 格式:
Key,Value
欢迎使用我的 Mod!,欢迎使用我的 Mod!
设置,设置
文档,Documentation.md
通过 LocalizationAssetsDir 属性,您可以指定多个本地化资源目录:
<PropertyGroup>
<LocalizationAssetsDir>assets/Locales;shared-locales;external-translations</LocalizationAssetsDir>
</PropertyGroup>
当指定多个目录时:
UpdateLocalesCsv target 会为所有目录生成和验证翻译文件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);
SDK 集成了 MessageHub 功能,允许 mod 之间进行通信,无需安装额外的 host mod。
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}");
// 处理消息...
});
}
}
var proxy = ModHttpV1Proxy.CreateFromSingleton();
await proxy.Notify("MyMod", "TargetModName", "custom_event", "Hello World!");
启用运行时方法补丁以实现高级 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>
<!-- 默认值:true(单 DLL 分发)-->
<EnableILRepack>true</EnableILRepack>
<!-- 禁用合并(依赖项复制到 Dependency/ 文件夹)-->
<EnableILRepack>false</EnableILRepack>
</PropertyGroup>
优点:
将复杂的 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=true)会跳过以下自动化任务:
preview.png)生成info.ini)生成保留的功能:
最终发布 Mod 项目(IsModLib=false 或未设置)会执行所有自动化任务。
IsModLib=true: 标记项目为共享库,跳过非必要的自动化任务ExcludeSdkLib=true: 排除 SDK 源代码编译(用于入口项目,避免重复编译)SDK 会自动生成:
开发期间禁用自动部署:
<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 # 中文翻译
└── ...
./scripts/packToLocal.sh --version 0.0.1
./scripts/rebuild_samples.sh
./scripts/fetch_build_dependency.sh
Samples/ 目录包含集成测试项目:
运行 ./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 源
| 属性 | 默认值 | 描述 |
|---|---|---|
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 和 LocalizationAssetsDir 有不同的用途:
AssetsDir: 用于一般资源(如 info.ini、preview.png 等),期望为单一路径LocalizationAssetsDir: 专用于本地化资源,支持多个目录(用分号分隔)使用场景:
<!-- 使用默认设置(两者相同) -->
<PropertyGroup>
<AssetsDir>assets</AssetsDir>
<!-- LocalizationAssetsDir 会自动使用 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 = "说明内容";
本地开发: 在项目根目录创建 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/
清除所有缓存并重建:
./scripts/rebuild_samples.sh --clear-all-caches
或手动清除:
dotnet nuget locals all --clear
rm -rf ~/.nuget/packages/ducky.sdk/
下载所需的游戏 DLL:
./scripts/fetch_build_dependency.sh
$(DuckovFolder) 路径存在$(DeployMod) 是否设置为 falseSDK 会验证 CSV 文件包含所有键。运行:
dotnet build
检查构建输出中的验证错误。
查看 Samples/ 目录获取完整示例:
欢迎贡献!请:
./scripts/rebuild_samples.sh 测试本项目采用 MIT 许可证 - 详见 LICENSE 文件。
用 ❤️ 为 Escape from Duckov Mod 社区打造