ET 框架 HybridCLR 热更新集成包:包结构、运行时加载链路与编辑器工具链详解
【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET
导读
cn.etetet.hybridclr是 ET 框架中集成 HybridCLR 的官方包,为 Unity 客户端提供基于 il2cpp 改造的原生 C# 热更新能力。本文以 AGENTS.md 为核心骨架,结合仓库源码深入讲解该包的目录约定、运行时加载链路(补充元数据装载 → 程序集加载 → 入口启动)、运行时 API 配置项以及编辑器生成工具链,并给出代码规范(et-code)与构建验证(et-build)的使用约定,帮助读者理解并正确接入 ET + HybridCLR 热更新体系。
一、包概览:cn.etetet.hybridclr 在 ET 中的定位
按照 AGENTS.md 的概述,该包是HybridCLR 集成包,包含热更新相关的运行时、插件和编辑器工具三大部分。从 package.json 可以看到包的元信息:
| 字段 | 值 | 说明 |
|---|---|---|
name | cn.etetet.hybridclr | 包唯一标识,遵循 ET 包命名规范 |
version | 8.5.1 | 当前仓库集成的 HybridCLR 版本 |
displayName | ET.HybridCLR | Unity 编辑器中的显示名 |
category | Runtime | 归类为运行时包 |
keywords | HybridCLR / hotupdate / hotfix / focus-creative-games / code-philosophy | 检索关键词 |
HybridCLR 本身是对 il2cpp 运行时的扩充:它将纯 AOT(预编译)runtime 改造成"AOT + Interpreter"混合 runtime,从而原生支持动态加载 assembly,从底层实现 C# 热更新。ET 通过这个包把 HybridCLR 的运行时(Runtime/)、原生插件(Plugins/)、编辑器生成与打包工具(Scripts/Editor/)以及安装器(Editor/Installer等)统一收纳为一个可随包分发的 Unity Package。
仓库中该包的完整演进记录见 RELEASELOG.md,最新条目为 8.5.1(2025-08-25),修复了值类型上System.Activator.CreateInstance<T>()的 instinct 变换栈计算缺陷,并修复了 PInvokeAnalyzer 对 PInvoke 函数调用约定的计算问题;8.4.0 起支持自定义 image 格式,8.1.0 用std::unordered_set优化了Assembly.Load的耗时(降为原来的约 33%)。这些条目也印证了该包是"运行时 + 编辑器"双线并进维护的。
二、HybridCLR 技术原理:从纯 AOT 到 AOT + Interpreter 混合运行时
理解本包的目录结构与工具链,需要先掌握 HybridCLR 的核心原理。根据 README.md 的说明,HybridCLR 受 mono 的 mixed mode execution 技术启发,为 il2cpp 这类 AOT runtime 额外提供 interpreter 模块,使其从纯 AOT 变为"AOT + Interpreter"混合运行方式。具体做了五方面工作:
- 实现高效的元数据(dll)解析库;
- 改造元数据管理模块,实现元数据的动态注册(补充元数据机制);
- 实现IL 指令集到自定义寄存器指令集的 compiler(即 IL 转换器);
- 实现高效的寄存器解释器(interpreter 执行引擎);
- 提供大量instinct 函数,提升解释器性能。
从使用者视角看,这些机制带来的直接能力包括:热更新代码与 AOT 代码无缝协作、支持继承/泛型/反射/多线程(volatile、ThreadStatic、async Task 等)、支持热更新 MonoBehaviour 与 ScriptableObject、支持MonoPInvokeCallback/PInvoke与原生代码交互、支持 DHE 差分混合执行与热重载等。版本支持方面,仓库 README 声明支持 2019.4.x、2020.3.x、2021.3.x、2022.3.x、2023.2.x、6000.x.y 等 LTS 版本以及所有 il2cpp 支持的平台(含团结引擎与鸿蒙平台)——具体以仓库实际集成的 8.5.1 为准。
在 ET 中,这套原理最终落到一个关键 API:HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(详见第四节)。
三、目录约定详解:编辑器代码、运行时与原生插件的组织方式
AGENTS.md 的「目录约定」是理解本包结构的关键,仓库实际目录如下:
Packages/cn.etetet.hybridclr/ ├── AGENTS.md / README.md / README_EN.md / RELEASELOG.md # 包文档与版本日志 ├── Data~/ # 编辑器工具使用的原生数据(.dll/.tpl/.json 等) ├── Editor/ # 原生工具与历史编辑器资源(UnityHook/7zip/UnityFS 等) ├── HybridCLR/ │ └── AssemblyReferenceToLoader.asmref # 程序集引用:汇入 ET.Loader ├── Plugins/ # dnlib.dll、LZ4.dll 等托管依赖 ├── Runtime/ # 运行时 API 与 ET.HybridCLR.asmdef └── Scripts/ ├── Editor/ # 编辑器 C# 代码(Commands/BuildProcessors/Settings 等) └── Model/ # 共享模型(如 PackageType.cs)3.1Scripts/Editor:编辑器代码统一落点
按 AGENTS.md 约定,编辑器代码放在Scripts/Editor下,并通过AssemblyReference.asmref汇入ET.Editor;同时明确"不再使用包根Editor目录承载编辑器 C# 代码"。对照仓库实际布局,包根Editor/目录保留的是非 C# 的原生资源与工具(如3rds/UnityHook下的Utils.cpp、libMonoHookUtils_OSX.dylib及构建脚本,3rds/7zip、3rds/UnityFS等第三方组件),而全部编辑器 C# 源码都集中在 Scripts/Editor/Share 目录树中:
Settings/:HybridCLRSettings.cs(配置项定义)、HybridCLRSettingProvider.cs(Project Settings 面板)、MenuProvider.cs(菜单项);Commands/:PrebuildCommand、CompileDllCommand、StripAOTDllCommand、AOTReferenceGeneratorCommand、LinkGeneratorCommand、MethodBridgeGeneratorCommand、Il2CppDefGeneratorCommand;BuildProcessors/:CheckSettings、FilterHotFixAssemblies、PatchScriptingAssemblyList、CopyStrippedAOTAssemblies、ScriptingAssembliesJsonPatcher、AddLil2cppSourceCodeToXcodeproj*(按 Unity 2019/2020-2021/2022/2023+ 分版本实现);AOT/:AOTAssemblyMetadataStripper(裁剪 AOT dll)、Analyzer、GenericReferenceWriter;Link/、MethodBridge/、Il2CppDef/、Meta/、ABI/、Installer/、HotUpdate/(MissingMetadataChecker)、Template/等模块目录。
这种"包根只放原生资源、C# 编辑器代码全部收进Scripts/Editor并由 asmref 汇入ET.Editor"的组织方式,是为了让包内的编辑器代码真正进入 ET 的主编辑器程序集,从而能调用 ET.Editor 提供的构建管线与工具函数。
3.2HybridCLR/AssemblyReferenceToLoader.asmref:运行时汇入 ET.Loader
包根下还有一个关键程序集引用文件 AssemblyReferenceToLoader.asmref,它把本包的运行时程序集(ET.HybridCLR.asmdef)汇入ET.Loader。这正是 CodeLoader.cs 中可以直接调用HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(...)的编译期前提——运行时加载逻辑与 HybridCLR API 在同一程序集内(详见第四节)。
3.3Runtime:运行时 API 与程序集定义
Runtime 目录包含运行所需的全部 API 文件:
| 文件 | 职责 |
|---|---|
ET.HybridCLR.asmdef | 运行时程序集定义 |
RuntimeApi.cs | 热更新入口 API(补充元数据装载、预编译、运行参数读写) |
RuntimeOptionId.cs | 解释器运行参数枚举(栈大小、方法体缓存、内联深度等) |
HomologousImageMode.cs | 补充元数据装载模式 |
LoadImageErrorCode.cs | 装载返回的错误码 |
ReversePInvokeWrapperGenerationAttribute.cs | 反向 P/Invoke 包装生成标记 |
3.4Plugins:托管第三方依赖
Plugins 目录提供编辑器阶段的托管依赖:dnlib.dll(.NET 元数据处理库,供Meta/、MethodBridge/、Link/等编辑器模块解析程序集)与LZ4.dll(LZ4 压缩,供3rds/UnityFS处理 AssetBundle 相关格式)。
四、运行时加载链路:CodeLoader 与补充元数据装载
AGENTS.md 概述中提到的"运行时"能力,在 ET 中由 CodeLoader.cs 承担具体落地。其Start()方法是热更新加载的完整链路,可拆分为四个阶段:
阶段一:下载热更新资源。await DownloadAsync()获取 dll/pdb 等热更资源(见 CodeLoader.cs)。
阶段二:装载 AOT 补充元数据(核心热更步骤)。在非编辑器环境(#if !UNITY_EDITOR)下,遍历this.aotDlls中的每个 TextAsset,调用:
HybridCLR.RuntimeApi.LoadMetadataForAOTAssembly(textAsset.bytes, HybridCLR.HomologousImageMode.SuperSet);见 CodeLoader.cs。这一步的作用是把AOT 程序集的补充元数据动态注册进 il2cpp 元数据系统,使热更新代码可以正常使用泛型实例化、反射等需要完整元数据的特性。装载模式使用HomologousImageMode.SuperSet(超集模式),即补充元数据 dll 为原始 AOT dll 的超集;LoadImageErrorCode枚举则定义了装载失败时的错误码(如 OK/错误码,见 LoadImageErrorCode.cs)。
阶段三:动态加载热更新程序集。依次Assembly.Load(modelAssBytes, modelPdbBytes)加载ET.Model/ET.ModelView,并通过LoadHotfix()加载ET.Hotfix/ET.HotfixView(pdb 同时加载以保留栈调试行号信息),见 CodeLoader.cs 与 CodeLoader.cs。
阶段四:装配并启动入口。将加载出的程序集列表交给CodeTypes单例注册类型,然后通过反射调用热更新程序集中的ET.Entry.Start启动游戏逻辑(见 CodeLoader.cs)。
需要特别注意的是,仓库代码注释提醒:编辑器调试时可改用File.ReadAllBytes(Path.Combine(Define.CodeDir, "ET.Model.dll.bytes"))直接读取本地生成的热更 dll(见 CodeLoader.cs),但真正打包发布时必须使用下载/内置的热更资源路径。
五、运行时 API 与解释器运行参数
RuntimeApi.cs 是热更新运行时的主要编程接口,按功能分组如下:
补充元数据装载。LoadMetadataForAOTAssembly(byte[] dllBytes, HomologousImageMode mode)返回LoadImageErrorCode。注意它在编辑器下是空实现(return LoadImageErrorCode.OK),仅在真机/发布环境通过[MethodImpl(MethodImplOptions.InternalCall)]桥接到 native 层。
预编译(Prejit)。PreJitMethod(MethodInfo method)与PreJitClass(Type type)用于在游戏启动时提前 JIT 解释器方法,规避首次调用时的解释开销;返回true表示成功预编译,false表示无法预编译。编辑器下同样为空实现。
运行参数读写。通过GetRuntimeOption(RuntimeOptionId)/SetRuntimeOption(RuntimeOptionId, int)读写解释器参数,并封装了如GetInterpreterThreadObjectStackSize()等便捷方法。RuntimeOptionId.cs 定义了全部参数:
| 枚举值 | 数值 | 含义 |
|---|---|---|
InterpreterThreadObjectStackSize | 1 | 解释器线程对象栈的 StackObject 数量上限(实际内存约 size×8 字节) |
InterpreterThreadFrameStackSize | 2 | 解释器线程帧栈大小 |
ThreadExceptionFlowSize | 3 | 线程异常流转栈大小 |
MaxMethodBodyCacheSize | 4 | 方法体缓存上限 |
MaxMethodInlineDepth | 5 | 方法内联最大深度 |
MaxInlineableMethodBodySize | 6 | 可内联方法体的最大体积 |
这些参数与 RELEASELOG 中 7.0.0(新增方法内联)与 7.1.0(默认MaxInlineableMethodBodySize由 16 调整为 32)的演进一一对应,可以在游戏启动早期按项目负载特征调优。
反向 P/Invoke。ReversePInvokeWrapperGenerationAttribute用于标记需要生成反向 P/Invoke 包装的委托,配合编辑器端MethodBridge生成器支持从 native(如 Lua/JS)回调热更新 C# 方法。
六、编辑器工具链:生成命令、打包处理器与配置项
6.1 命令菜单(Commands)
编辑器菜单HybridCLR/Generate/*对应 Commands 下的各生成命令,典型工作流为:
PrebuildCommand—— 生成前的预处理(如检查 HybridCLR 是否已安装);CompileDllCommand—— 把热更新程序集编译为目标平台的 dll;StripAOTDllCommand—— 裁剪 AOT 程序集,产出补充元数据所需的精简 dll(配合AOTAssemblyMetadataStripper去除非泛型函数元数据);AOTReferenceGeneratorCommand—— 分析热更新程序集对 AOT 泛型类型/方法的引用,生成补充元数据清单(AOTGenericReferences.cs);LinkGeneratorCommand—— 扫描热更新程序集自动生成link.xml,防止 AOT 链接裁剪掉运行所需的元数据;MethodBridgeGeneratorCommand—— 生成托管/native 双向调用的桥接函数(MethodBridge);Il2CppDefGeneratorCommand—— 生成 il2cpp 定义相关代码(UnityVersion.h.tpl、AssemblyManifest.cpp.tpl、MethodBridge.cpp.tpl等模板来自Data~目录)。
6.2 打包处理器(BuildProcessors)
BuildProcessors 通过 UnityIPreprocessBuildWithReport等回调在打包阶段自动工作:CheckSettings校验 Scripting Backend 必须为 il2cpp 及 API 兼容级别;FilterHotFixAssemblies从构建中过滤热更程序集;PatchScriptingAssemblyList/ScriptingAssembliesJsonPatcher修正scriptingassemblies.json,把热更程序集从 AOT 编译列表剔除;CopyStrippedAOTAssemblies拷贝裁剪后的 AOT dll 供运行时装载补充元数据;AddLil2cppSourceCodeToXcodeproj*负责在 iOS/macOS 导出 Xcode 工程时把 HybridCLR 的 native 源码(libil2cpp 扩展)注入工程。
6.3 配置项(HybridCLRSettings)
编辑器配置定义在 HybridCLRSettings.cs,序列化存储于 Unity 工程根目录ProjectSettings/HybridCLRSettings.asset(见该文件GetFilePath()实现,L71-L74),并通过HybridCLRSettingProvider在 Project Settings 面板中展示。核心字段及默认值:
| 字段 | 默认值 | 说明 |
|---|---|---|
enable | true | 是否启用 HybridCLR |
useGlobalIl2cpp | false | 是否使用 Unity 安装目录中的全局 il2cpp |
hybridclrRepoURL/il2cppPlusRepoURL | gitee 仓库地址 | 安装器拉取 hybridclr / il2cpp_plus 源码的仓库 URL |
hotUpdateAssemblyDefinitions | — | 热更新程序集的 asmdef 资源列表 |
hotUpdateAssemblies | — | 热更新程序集名列表(不带 .dll 后缀) |
preserveHotUpdateAssemblies | — | 需保留的热更新程序集名 |
hotUpdateDllCompileOutputRootDir | HybridCLRData/HotUpdateDlls | 热更 dll 编译输出目录 |
externalHotUpdateAssembliyDirs | — | 外部热更程序集搜索路径 |
strippedAOTDllOutputRootDir | HybridCLRData/AssembliesPostIl2CppStrip | 裁剪后 AOT dll 输出目录 |
patchAOTAssemblies | — | 补充元数据程序集名列表 |
outputLinkFile | HybridCLRGenerate/link.xml | 自动生成的 link.xml 输出路径 |
outputAOTGenericReferenceFile | HybridCLRGenerate/AOTGenericReferences.cs | 自动生成的泛型引用清单输出路径 |
maxGenericReferenceIteration | 10 | 热更程序集中泛型方法搜索的最大迭代次数 |
maxMethodBridgeGenericIteration | 10 | AOT 程序集中方法桥接泛型搜索的最大迭代次数 |
6.4 安装器与体检工具
Installer(InstallerWindow/InstallerController/BashUtil)提供图形化安装入口,负责把 HybridCLR 的 libil2cpp 扩展源码下载并接入 Unity 安装目录;HotUpdate/MissingMetadataChecker则用于在开发期检测"热更新代码引用了 AOT 泛型但缺少补充元数据"等典型配置遗漏,避免发布后才暴露运行时错误。
七、代码规范与构建验证:AGENTS.md 引用的两个 skill
AGENTS.md 的「详细文档」部分将本包的开发工作流锚定到 harness 的两个 skill,这两者共同构成 HybridCLR 包的维护规范:
7.1 代码规范:/et-code
et-code SKILL.md 覆盖 ET 框架 C# 代码编写与审查流程,对本包(以及依赖它的热更新模块)的约束要点包括:
- 新建或修改
Entity/Component/System/Helper、消息 Handler、程序集与包依赖时启用;纯测试、配置导出或 Unity 编辑器操作场景不加载该 skill; - 改动前先确认代码所在 package、程序集层级和包内
AGENTS.md规则;判断是否影响 Entity/System 分离、HandlerRun规范、包依赖单向与 Module analyzer; - 涉及
async/await/ETTask/EntityRef时叠加 et-async skill; - 默认"一类一文件";严禁 AI 手工生成
.meta或手工修改.csproj,移动 C# 文件必须同步移动.meta以保留 GUID——这与本包"目录约定"中 asmref/asmdef 的组织方式直接相关; - 输出要求:说明代码落点、包依赖、程序集层次是否正确,是否影响
.meta/.csproj/ Unity 刷新或工程文件生成,是否引入新的 analyzer 风险、静态状态风险或模块边界问题。
7.2 构建验证:/et-build
et-build SKILL.md 提供编译、Proto 导出、服务器启动与发布的唯一标准入口:
- 编译与分析器验证统一使用
dotnet build ET.sln,不单独编译包或 IDE 私有方案; - Proto 导出:
dotnet ./Bin/ET.Proto2CS.dll; - 启动服务器:
dotnet ./Bin/ET.App.dll --Console=1(必须在 Unity 项目根目录启动,不在Bin/目录启动,运行前先清理旧Logs/); - 发布:
pwsh -ExecutionPolicy Bypass -File ./Scripts/Publish.ps1; - 特别提醒:Model / Hotfix 程序集不能用 IDE 编译,必须走项目规定的 Unity / ET 编译入口——这正是 HybridCLR 热更 dll 由编辑器
CompileDllCommand生成的原因。
八、实践小结与接入注意事项
综合以上分析,在 ET 项目中接入cn.etetet.hybridclr时应把握以下要点:
- 结构认知:编辑器 C# 代码统一在 Scripts/Editor,通过 asmref 汇入
ET.Editor;运行时通过 AssemblyReferenceToLoader.asmref 汇入ET.Loader;包根Editor/只保留原生工具资源。 - 加载顺序不可颠倒:真机加载必须"先装载 AOT 补充元数据(
HomologousImageMode.SuperSet)→ 再Assembly.Load热更程序集 → 最后反射调用ET.Entry.Start",参见 CodeLoader.cs。 - 配置与生成配套:
HybridCLRSettings中的热更程序集列表、补充元数据列表、link.xml 与 AOTGenericReferences 输出路径必须与Generate/*命令的执行结果保持一致,否则会出现 MissingMetadata / 链接裁剪类问题。 - 发布约束:以仓库实际集成的 8.5.1 版本为准,关注 RELEASELOG.md 中的关键修复(如打包时
Texture Compression设置被改动、WebGL 平台scriptingassemblies.json路径等问题),升级包后需重新执行安装器并重新生成。 - 开发纪律:遵循
et-code的代码规范(.meta 同步、一类一文件、包依赖单向)与et-build的构建入口(dotnet build ET.sln、Publish.ps1),避免绕过既定管线导致热更链路不一致。
【免费下载链接】ETUnity3D Client And C# Server Framework项目地址: https://gitcode.com/GitHub_Trending/et/ET
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考