From 3ac6125e431347d8aa994287f08bc4e106f0c066 Mon Sep 17 00:00:00 2001 From: walon Date: Fri, 26 Jan 2024 21:50:32 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=97=97=E8=88=B0=E7=89=88?= =?UTF-8?q?=E6=96=87=E6=A1=A3=EF=BC=8C=E5=8C=85=E5=90=AB=E5=B8=A6=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=92=8C=E4=B8=8D=E5=B8=A6=E6=A0=A1=E9=AA=8C=E7=9A=84?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/business/ultimate/manual.md | 59 ++- .../{quickstart.md => quickstartchecked.md} | 16 +- docs/business/ultimate/quickstartunchecked.md | 376 ++++++++++++++ .../current/business/ultimate/manual.md | 72 ++- .../current/business/ultimate/quickstart.md | 468 ----------------- .../business/ultimate/quickstartchecked.md | 478 ++++++++++++++++++ .../business/ultimate/quickstartunchecked.md | 376 ++++++++++++++ sidebars.js | 3 +- 8 files changed, 1356 insertions(+), 492 deletions(-) rename docs/business/ultimate/{quickstart.md => quickstartchecked.md} (96%) create mode 100644 docs/business/ultimate/quickstartunchecked.md delete mode 100644 i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstart.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstartchecked.md create mode 100644 i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstartunchecked.md diff --git a/docs/business/ultimate/manual.md b/docs/business/ultimate/manual.md index 257f2282f..db9de7d91 100644 --- a/docs/business/ultimate/manual.md +++ b/docs/business/ultimate/manual.md @@ -92,7 +92,7 @@ dhao文件是DHE技术的核心概念。dhao文件中包含了离线计算好的 ## 代码中使用 -运行时,完成热更新后,对于每个dhe程序集,调用 `RuntimeApi::LoadDifferentialHybridAssembly` 加载热更新assembly。 +运行时,完成热更新后,对于每个dhe程序集,调用 `RuntimeApi::LoadDifferentialHybridAssembly`或`RuntimeApi::LoadDifferentialHybridAssemblyUnchecked` 加载热更新assembly。 注意事项: @@ -100,6 +100,17 @@ dhao文件是DHE技术的核心概念。dhao文件中包含了离线计算好的 - 如果某个程序集未发生改变,dhao字段可以传null,但此时一定要使用打包时生成的AOT dll,而不能使用通过`HybridCLR/CompileDll/xxx`命令生成的热更新dll。 - DHE程序集本身已经包含了元数据,即使未开启完全泛型共享时也**不要对DHE程序集进行补充元数据**,补充了也会失败,其他非DHE的AOT程序集可以照常补充元数据。 + +`RuntimeApi::LoadDifferentialHybridAssembly`为带校验的工作流,需要传入原始dhe dll的md5及当前dhe dll的md5,与dhao文件中保存的md5进行对比。 +为了originalDllMd5和currentDllMd5参数,极大增加了工作流的复杂度。 + +:::tip + +推荐初学者在demo项目中使用带校验的工作流,熟悉工作流后在正式项目中使用不带校验的工作流。 +::: + +### 带校验的 `RuntimeApi::LoadDifferentialHybridAssembly` + ```csharp title="加载DHE程序集" public static string CreateMD5Hash(byte[] bytes) @@ -126,13 +137,34 @@ void LoadDifferentialHybridAssembly(string assemblyName, string originalDllMd5) } ``` +### 不带校验的 `RuntimeApi::LoadDifferentialHybridAssemblyUnchecked` + :::warning -`RuntimeApi::LoadDifferentialHybridAssemblyUnchecked` 函数不需要提供originalDllMd5和currentDllMd5参数,使用较为便利,但强烈不建议使用。 -实践中经常由于操作失误,使用了错误的原始dll或者热更新dll,导致生成了错误的dhao文件。使用了错误的dhao文件,轻则运行出错,重则进程崩溃。 +使用不带校验的工作流,请务必保证原始dhe dll、当前dhe dll和dhao文件的一致性。如果不一致,轻则运行出错,重则进程崩溃。 ::: +```csharp title="加载DHE程序集" + +/// +/// originalDllMd5 从构建时生成的`{manifest}`清单文件中获得,此清单文件由开发者自己生成 +/// +void LoadDifferentialHybridAssembly(string assemblyName) +{ + LoadImageErrorCode err = RuntimeApi.LoadDifferentialHybridAssemblyUnchecked(dllBytes, dhaoBytes); + if (err == LoadImageErrorCode.OK) + { + Debug.Log($"LoadDifferentialHybridAssembly {assName} OK"); + } + else + { + Debug.LogError($"LoadDifferentialHybridAssembly {assName} failed, err={err}"); + } +} +``` + + ## 配置函数注入策略 :::tip @@ -159,9 +191,16 @@ DHE技术中与构建相关的文件为dhe dll文件和对应的dhao文件。 - 将构建后生成的裁剪AOT dll作为 首包(没有任何改动)的dhe dll - 使用`HybridCLR.Editor.DHE.BuildUtils.GenerateUnchangedDHAODatas`生成首包的dhao文件 -- 为dhe文件生成一个至少包含 assemblyName,md5的`{manifest}`清单文件(由开发者自由决定怎么实现),因为`RuntimeApi.LoadDifferentialHybridAssembly`需要提供dhe dll的原始md5 + +如果使用带校验的工作流,则执行以下操作: + +- 为dhe dll生成一个至少包含 assemblyName,md5的`{manifest}`清单文件(由开发者自由决定怎么实现),因为`RuntimeApi.LoadDifferentialHybridAssembly`需要提供dhe dll的原始md5 - 将 dhe dll、dhao文件及`{manifest}`文件加入热更新资源管理系统 +如果使用不带校验的工作流,则执行以下操作: + +- 将 dhe dll、dhao文件加入热更新资源管理系统 + 如果想随包携带首包的dhe dll和dhao文件,请先导出工程,再按照上面的步骤生成dhe dll和dhao文件,再将它们加入到导出工程中。 @@ -181,11 +220,17 @@ DHE技术中与构建相关的文件为dhe dll文件和对应的dhao文件。 #### 构建主包 -- 将构建后生成的裁剪AOT dll作为 首包(没有任何改动)的dhe dll +- 将构建后生成的裁剪AOT dll作为 原始dhe dll - 使用`HybridCLR.Editor.DHE.BuildUtils.EncryptDllAndGenerateUnchangedDHAODatas`生成首包的dhao文件及加密后的dhe dll文件 -- 将 dhe dll和dhao文件加入热更新资源管理系统 -如果想随包携带首包的dhe dll和dhao文件,请先导出工程,再按照上面的步骤生成dhe dll和dhao文件,再将它们加入到导出工程中。 +如果使用带校验的工作流,则执行以下操作: + +- 为dhe dll生成一个至少包含 assemblyName,md5的`{manifest}`清单文件(由开发者自由决定怎么实现),因为`RuntimeApi.LoadDifferentialHybridAssembly`需要提供dhe dll的原始md5 +- 将 加密后的dhe dll、dhao文件及`{manifest}`文件加入热更新资源管理系统 + +如果使用不带校验的工作流,则执行以下操作: + +- 将 加密后的dhe dll、dhao文件加入热更新资源管理系统 #### 热更新 diff --git a/docs/business/ultimate/quickstart.md b/docs/business/ultimate/quickstartchecked.md similarity index 96% rename from docs/business/ultimate/quickstart.md rename to docs/business/ultimate/quickstartchecked.md index 98945c38a..c583618d0 100644 --- a/docs/business/ultimate/quickstart.md +++ b/docs/business/ultimate/quickstartchecked.md @@ -1,10 +1,21 @@ -# 快速上手 +# 快速上手(带校验的工作流) 本教程引导从空项目开始体验HybridCLR热更新。出于简化起见,只演示BuildTarget为**Windows**或**MacOS** Standalone平台的情况。 请在Standalone平台上正确跑通热更新流程后再自行尝试Android、iOS平台的热更新,它们的流程非常相似。 旗舰版本使用难度跟社区版本相似,大多数原理相同,建议先熟悉社区版本后再尝试旗舰版本。 +自v5.0.0版本起,同时支持带校验的`RuntimeApi.LoadDifferentialHybridAssembly`工作流和不带校验的`RuntimeApi.LoadDifferentialHybridAssemblyUnchecked`工作流。 +本文档介绍带校验的工作流。 + +:::tip + +实践中不带校验的工作流会简单很多,不必传递originalDllMd5和currentDllMd5参数,所以省去了工作流中保存或者计算dll md5的过程。 +但要求开发者确保aot dll、hot update dll、dhao文件的一致性。 +推荐初学者在demo项目中使用带校验的工作流,熟悉工作流后在正式项目中使用不带校验的工作流。 + +::: + ## 体验目标 - 创建热更新程序集 @@ -347,10 +358,9 @@ public class Hello 为了简化演示,我们不通过http服务器下载HotUpdate.dll,而是直接将HotUpdate.dll放到StreamingAssets目录下。 -HybridCLR是原生运行时实现,因此调用`Assembly Assembly.Load(byte[])`即可加载热更新程序集。 - 创建`Assets/LoadDll.cs`脚本,然后**在main场景中创建一个GameObject对象,挂载LoadDll脚本**。 + ```csharp using HybridCLR; using System; diff --git a/docs/business/ultimate/quickstartunchecked.md b/docs/business/ultimate/quickstartunchecked.md new file mode 100644 index 000000000..9aae2d2e8 --- /dev/null +++ b/docs/business/ultimate/quickstartunchecked.md @@ -0,0 +1,376 @@ +# 快速上手(不带校验的工作流) + +本教程引导从空项目开始体验HybridCLR热更新。出于简化起见,只演示BuildTarget为**Windows**或**MacOS** Standalone平台的情况。 +请在Standalone平台上正确跑通热更新流程后再自行尝试Android、iOS平台的热更新,它们的流程非常相似。 + +旗舰版本使用难度跟社区版本相似,大多数原理相同,建议先熟悉社区版本后再尝试旗舰版本。 + +自v5.0.0版本起,同时支持带校验的`RuntimeApi.LoadDifferentialHybridAssembly`工作流和不带校验的`RuntimeApi.LoadDifferentialHybridAssemblyUnchecked`工作流。 +本文档介绍不带校验的工作流。 + +:::tip + +实践中不带校验的工作流会简单很多,不必传递originalDllMd5和currentDllMd5参数,所以省去了工作流中保存或者计算dll md5的过程。 +但要求开发者确保aot dll、hot update dll、dhao文件的一致性。 +推荐初学者在demo项目中使用带校验的工作流,熟悉工作流后在正式项目中使用不带校验的工作流。 + +::: + +## 体验目标 + +- 创建热更新程序集 +- 加载热更新程序集,并执行其中热更新代码,打印 `Hello, HybridCLR` +- 修改热更新代码,打印 `Hello, World` + +## 准备环境 + +### 安装Unity + +- 安装 2019.4.x、2020.3.x、2021.3.x、2022.3.x 中任一版本。某些版本有特殊的安装要求,参见[安装hybridclr](../../basic/install.md) +- 根据你所用的操作系统,安装过程中选择模块时,必须选中 `Windows Build Support(IL2CPP)`或`Mac Build Support(IL2CPP)` + +![select il2cpp modules](/img/hybridclr/select_il2cpp_modules.jpg) + +### 安装IDE及相关编译环境 + +- Windows + - Win下需要安装`visual studio 2019`或更高版本。安装时至少要包含 `使用Unity的游戏开发` 和 `使用c++的游戏开发` 组件 + - 安装git +- Mac + - 要求MacOS版本 >= 12,xcode版本 >= 13,例如`xcode 13.4.1, macos 12.4` + - 安装 git + +## 初始化Unity热更新项目 + +从零开始构造热更新项目的过程较冗长,以下步骤中涉及的代码可参考dhe_demo项目,其仓库地址为 [github](https://github.com/focus-creative-games/dhe_demo) 。 + +### 创建项目 + +创建空的Unity项目。 + +### 创建`ConsoleToScreen.cs`脚本 + +这个脚本对于演示热更新没有直接作用。它可以打印日志到屏幕上,方便定位错误。 + +创建 `Assets/ConsoleToScreen.cs` 脚本类,代码如下: + +```csharp +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ConsoleToScreen : MonoBehaviour +{ + const int maxLines = 50; + const int maxLineLength = 120; + private string _logStr = ""; + + private readonly List _lines = new List(); + + public int fontSize = 15; + + void OnEnable() { Application.logMessageReceived += Log; } + void OnDisable() { Application.logMessageReceived -= Log; } + + public void Log(string logString, string stackTrace, LogType type) + { + foreach (var line in logString.Split('\n')) + { + if (line.Length <= maxLineLength) + { + _lines.Add(line); + continue; + } + var lineCount = line.Length / maxLineLength + 1; + for (int i = 0; i < lineCount; i++) + { + if ((i + 1) * maxLineLength <= line.Length) + { + _lines.Add(line.Substring(i * maxLineLength, maxLineLength)); + } + else + { + _lines.Add(line.Substring(i * maxLineLength, line.Length - i * maxLineLength)); + } + } + } + if (_lines.Count > maxLines) + { + _lines.RemoveRange(0, _lines.Count - maxLines); + } + _logStr = string.Join("\n", _lines); + } + + void OnGUI() + { + GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, + new Vector3(Screen.width / 1200.0f, Screen.height / 800.0f, 1.0f)); + GUI.Label(new Rect(10, 10, 800, 370), _logStr, new GUIStyle() { fontSize = Math.Max(10, fontSize) }); + } +} + + +``` + +### 创建主场景 + +- 创建默认初始场景 main.scene +- 场景中创建一个空GameObject,将ConsoleToScreen挂到上面 +- 在`Build Settings`中添加main场景到打包场景列表 + +### 创建 HotUpdate 热更新模块 + +- 创建 `Assets/HotUpdate` 目录 +- 在目录下 右键 `Create/Assembly Definition`,创建一个名为`HotUpdate`的程序集模块 + +## 安装和配置HybridCLR + +### 安装 + +- 将hybridclr_unity.zip解压后,放到项目Packages目录下,改名为com.code-philosophy.hybridclr +- 根据你的unity版本解压对应的`libil2cpp-{version}.7z` +- 打开 `HybridCLR/Installer`,启用`从本地复制libil2cpp`选项,选中刚才解压的libil2cpp目录,进行安装 +- 根据你的Unity版本: + - 如果版本 >= 2020,将 `ModifiedDlls\{verions}\Unity.IL2CPP.dll` 文件替换 `{proj}\HybridCLRData\LocalIl2CppData-WindowsEditor\il2cpp\build\deploy\netcoreapp3.1\Unity.IL2CPP.dll`(Unity 2020)或`{proj}\HybridCLRData\LocalIl2CppData-WindowsEditor\il2cpp\build\deploy\Unity.IL2CPP.dll`(Unity 2021+)。如果没有你的版本对应的文件,联系我们制作一个 + - 如果版本 为 2019,不需要任何操作,因为Install过程中已经自动复制 + +![installer](/img/hybridclr/ultimate-installer.jpg) + +### 配置HybridCLR + +- 打开菜单 `HybridCLR/Settings` +- 在`differentialHybridAssemblies`列表中添加`HotUpdate`程序集 + +![settings](/img/hybridclr/ultimate-hybridclr-settings.jpg) + +### 配置PlayerSettings + +- `Scripting Backend` 切换为 `IL2CPP` +- `Api Compatability Level` 切换为 `.Net 4.x`(Unity 2019-2020) 或 `.Net Framework`(Unity 2021+) + +![player settings](/img/hybridclr/ultimate-project-settings.jpg) + +## 创建Editor脚本 + +在`Assets/Editor`目录下创建 BuildTools.cs 文件,内容如下: + +```csharp + +using HybridCLR.Editor; +using HybridCLR.Editor.DHE; +using HybridCLR.Runtime; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEditor; +using UnityEngine; + +public static class BuildTools +{ + public const string BackupAOTDllDir = "HybridCLRData/BackupAOT"; + + public const string DhaoDir = "HybridCLRData/Dhao"; + + + /// + /// 备份构建主包时生成的裁剪AOT dll + /// + [MenuItem("BuildTools/BackupAOTDll")] + public static void BackupAOTDllFromAssemblyPostStrippedDir() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + var backupDir = $"{BackupAOTDllDir}/{target}"; + System.IO.Directory.CreateDirectory(backupDir); + var dlls = System.IO.Directory.GetFiles(SettingsUtil.GetAssembliesPostIl2CppStripDir(target)); + foreach (var dll in dlls) + { + var fileName = System.IO.Path.GetFileName(dll); + string dstFile = $"{BackupAOTDllDir}/{target}/{fileName}"; + System.IO.File.Copy(dll, dstFile, true); + Debug.Log($"BackupAOTDllFromAssemblyPostStrippedDir: {dll} -> {dstFile}"); + } + } + + /// + /// 生成首包的没有任何代码改动对应的dhao数据 + /// + [MenuItem("BuildTools/GenerateUnchangedDHAODatas")] + public static void GenerateUnchangedDHAODatas() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + BuildUtils.GenerateUnchangedDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, dhaoDir); + } + + /// + /// 生成热更包的dhao数据 + /// + [MenuItem("BuildTools/GenerateDHAODatas")] + public static void GenerateDHAODatas() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + string currentDllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); + BuildUtils.GenerateDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, currentDllDir, dhaoDir); + } + + /// + /// 复制没有改动的首包dll和dhao文件到StreamingAssets + /// + [MenuItem("BuildTools/CopyUnchangedDllAndDhaoFileToStreamingAssets")] + public static void CopyUnchangedDllAndDhaoFileToStreamingAssets() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string streamingAssetsDir = Application.streamingAssetsPath; + Directory.CreateDirectory(streamingAssetsDir); + + string dllDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + foreach (var dll in SettingsUtil.DifferentialHybridAssemblyNames) + { + string srcFile = $"{dllDir}/{dll}.dll"; + string dstFile = $"{streamingAssetsDir}/{dll}.dll.bytes"; + System.IO.File.Copy(srcFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {srcFile} -> {dstFile}"); + string dhaoFile = $"{dhaoDir}/{dll}.dhao.bytes"; + dstFile = $"{streamingAssetsDir}/{dll}.dhao.bytes"; + System.IO.File.Copy(dhaoFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {dhaoFile} -> {dstFile}"); + } + } + + /// + /// 复制热更新dll和dhao文件到StreamingAssets + /// + [MenuItem("BuildTools/CopyDllAndDhaoFileToStreamingAssets")] + public static void CopyDllAndDhaoFileToStreamingAssets() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string streamingAssetsDir = Application.streamingAssetsPath; + Directory.CreateDirectory(streamingAssetsDir); + + string dllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); + string dhaoDir = $"{DhaoDir}/{target}"; + foreach (var dll in SettingsUtil.DifferentialHybridAssemblyNames) + { + string srcFile = $"{dllDir}/{dll}.dll"; + string dstFile = $"{streamingAssetsDir}/{dll}.dll.bytes"; + System.IO.File.Copy(srcFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {srcFile} -> {dstFile}"); + string dhaoFile = $"{dhaoDir}/{dll}.dhao.bytes"; + dstFile = $"{streamingAssetsDir}/{dll}.dhao.bytes"; + System.IO.File.Copy(dhaoFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {dhaoFile} -> {dstFile}"); + } + } +} + + +``` + +## 创建热更新脚本 + +创建 `Assets/HotUpdate/Hello.cs` 文件,代码内容如下 + +```csharp +using System.Collections; +using UnityEngine; + +public class Hello +{ + public static void Run() + { + Debug.Log("Hello, HybridCLR"); + } +} +``` + +## 加载热更新程序集 + +为了简化演示,我们不通过http服务器下载HotUpdate.dll,而是直接将HotUpdate.dll放到StreamingAssets目录下。 + +创建`Assets/LoadDll.cs`脚本,然后**在main场景中创建一个GameObject对象,挂载LoadDll脚本**。 + + +```csharp +using HybridCLR; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Networking; + +public class LoadDll : MonoBehaviour +{ + + void Start() + { + // Editor环境下,HotUpdate.dll.bytes已经被自动加载,不需要加载,重复加载反而会出问题。 +#if !UNITY_EDITOR + Assembly hotUpdateAss = LoadDifferentialHybridAssembly("HotUpdate"); +#else + // Editor下无需加载,直接查找获得HotUpdate程序集 + Assembly hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate"); +#endif + Type helloType = hotUpdateAss.GetType("Hello"); + MethodInfo runMethod = helloType.GetMethod("Run"); + runMethod.Invoke(null, null); + } + + private Assembly LoadDifferentialHybridAssembly(string assName) + { + byte[] dllBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{assName}.dll.bytes"); + byte[] dhaoBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{assName}.dhao.bytes"); + LoadImageErrorCode err = RuntimeApi.LoadDifferentialHybridAssembly(dllBytes, dhaoBytes); + if (err == LoadImageErrorCode.OK) + { + Debug.Log($"LoadDifferentialHybridAssembly {assName} OK"); + return System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == assName); + } + else + { + Debug.LogError($"LoadDifferentialHybridAssembly {assName} failed, err={err}"); + return null; + } + } +} + +``` + + +至此,完成整个热更新工程的创建工作!!! + +## Editor中试运行 + +运行main场景,屏幕上会显示 'Hello,HybridCLR',表示代码工作正常。 + +## 打包运行 + +- 运行菜单 `HybridCLR/Generate/All` 进行必要的生成操作。**这一步不可遗漏**!!! +- 打开 `Build Settings` 对话框,点击`Build`,选择输出目录`{build}`,执行构建 +- 运行 `BuildTools/BackupAOTDll` 备份裁剪后的dhe dll。 实践中这些dll应该加入版本管理,用于后面生成dhao文件,这些文件不会再被修改 +- 运行 `BuildTools/GenerateUnchangedDHAODatas` 生成首包的dhao文件 +- 运行 `BuildTools/CopyUnchangedDllAndDhaoFileToStreamingAssets` 复制首包 dhe程序集、dhao文件到 StreamingAssets +- 将 `Assets/StreamingAssets`目录复制到`{build}\dhe_demo2_Data\StreamingAssets` +- 运行`{build}/Xxx.exe`,屏幕显示 `Hello,HybridCLR`,表示热更新代码被顺利执行! + +## 测试热更新 + +- 修改`Assets/HotUpdate/Hello.cs`的Run函数中`Debug.Log("Hello, HybridCLR");`代码,改成`Debug.Log("Hello, World");`。 +- 运行`HybridCLR/CompileDll/ActiveBulidTarget`生成热更新dll +- 运行`BuildTools/GenerateDHAODatas` 生成dhao文件 +- 运行`BuildTools/CopyDllAndDhaoFileToStreamingAssets`复制热更新dll和dhao文件到StreamingAssets目录 +- 将 `Assets/StreamingAssets`目录复制到`{build}\dhe_demo2_Data\StreamingAssets` +- 重新运行程序,会发现屏幕中显示`Hello, World`,表示热更新代码生效了! + + +至此完成热更新体验!!! diff --git a/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/manual.md b/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/manual.md index 5555a1bb2..d80f84641 100644 --- a/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/manual.md +++ b/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/manual.md @@ -1,4 +1,4 @@ -# Manual +# manual ## Install @@ -92,13 +92,24 @@ Unless there are special circumstances and you are an experienced expert, do not ## Used in code -At runtime, after hot update is completed, for each dhe assembly, call `RuntimeApi::LoadDifferentialHybridAssembly` to load the hot update assembly. +At runtime, after hot update is completed, for each dhe assembly, call `RuntimeApi::LoadDifferentialHybridAssembly` or `RuntimeApi::LoadDifferentialHybridAssemblyUnchecked` to load the hot update assembly. Precautions: - Differential hybrid execution assembly should be loaded according to assembly dependency order. - If an assembly has not changed, the dhao field can be passed as null, but in this case the AOT dll generated during packaging must be used, and the hot update dll generated through the `HybridCLR/CompileDll/xxx` command cannot be used. -- The DHE assembly itself already contains metadata. Even if full generic sharing is not enabled, do not supplement metadata for the DHE assembly. If you supplement it, it will fail. Other non-DHE AOT assemblies can be supplemented as usual. metadata. +- The DHE assembly itself already contains metadata. Even if full generic sharing is not enabled, **Do not add metadata** to the DHE assembly. If you add it, it will fail. Other non-DHE AOT assemblies can be added as usual. metadata. + + +`RuntimeApi::LoadDifferentialHybridAssembly` is a checked workflow. It needs to pass in the md5 of the original dhe dll and the md5 of the current dhe dll, and compare them with the md5 saved in the dhao file. +For the originalDllMd5 and currentDllMd5 parameters, the complexity of the workflow is greatly increased. + +:::tip + +It is recommended that beginners use the checked workflow in demo projects. After becoming familiar with the workflow, they can use the unchecked workflow in formal projects. +::: + +### use unchecked `RuntimeApi::LoadDifferentialHybridAssembly` ```csharp title="Loading DHE assembly" @@ -126,28 +137,50 @@ void LoadDifferentialHybridAssembly(string assemblyName, string originalDllMd5) } ``` +### use checked `RuntimeApi::LoadDifferentialHybridAssemblyUnchecked` + :::warning -The `RuntimeApi::LoadDifferentialHybridAssemblyUnchecked` function does not need to provide originalDllMd5 and currentDllMd5 parameters, which is more convenient to use, but its use is strongly not recommended. -In practice, incorrect dhao files are often generated due to operational errors and the use of the wrong original dll or hot update dll. If the wrong dhao file is used, it may cause an operation error or a process crash. +When using a unchecked workflow, be sure to ensure the consistency of the original dhe dll, current dhe dll, and dhao files. If they are inconsistent, an operation error may occur, or the process may crash. ::: -## Configure function injection rules +```csharp title="Loading DHE assembly" + +/// +/// originalDllMd5 is obtained from the `{manifest}` manifest file generated during the build. This manifest file is generated by the developer himself +/// +void LoadDifferentialHybridAssembly(string assemblyName) +{ + LoadImageErrorCode err = RuntimeApi.LoadDifferentialHybridAssemblyUnchecked(dllBytes, dhaoBytes); + if (err == LoadImageErrorCode.OK) + { + Debug.Log($"LoadDifferentialHybridAssembly {assName} OK"); + } + else + { + Debug.LogError($"LoadDifferentialHybridAssembly {assName} failed, err={err}"); + } +} +``` + + +## Configure function injection strategy :::tip -In the vast majority of projects, the default full injection rules has minimal impact on performance. As long as there are no performance issues, you do not need and should not care about this configuration. +In the vast majority of projects, the default full injection strategy has minimal impact on performance. As long as there are no performance issues, you do not need and should not care about this configuration. ::: In order to avoid indirect dirty function contagion (that is, function A calls function B, if B changes, A will also be marked as changed), a small piece of check jump code is injected into the header of all functions by default. Although it is Very simple `if (method->isInterpterImpl)` statement, but for short functions like `int Age {get; set;}`, this insertion may produce an observable performance degradation (even up to 10%). -The function injection rules is used to optimize this situation. For short functions that do not change, configuring not to inject can improve performance. See the [InjectRules](./injectrules) document for details. +The function injection strategy is used to optimize this situation. For short functions that do not change, configuring not to inject can improve performance. See the [InjectRules](./injectrules) document for details. Fill in the injection policy file path in the `InjectRuleFiles` field in `HybridCLR Settings`. The relative path of the file is the project root directory (such as `Assets/InjectRules/DefaultInjectRules.xml`). + ## Pack The files related to construction in DHE technology are dhe dll files and corresponding dhao files. @@ -158,9 +191,16 @@ The files related to construction in DHE technology are dhe dll files and corres - Use the trimmed AOT dll generated after building as the dhe dll of the first package (without any changes) - Use `HybridCLR.Editor.DHE.BuildUtils.GenerateUnchangedDHAODatas` to generate the dhao file of the first package -- Generate a `{manifest}` manifest file for the dhe file that contains at least assemblyName, md5 (it is up to the developer to decide how to implement it), because `RuntimeApi.LoadDifferentialHybridAssembly` needs to provide the original md5 of the dhe dll + +If using a checked workflow, do the following: + +- Generate a `{manifest}` manifest file for dhe dll that contains at least assemblyName, md5 (it is up to the developer to decide how to implement it), because `RuntimeApi.LoadDifferentialHybridAssembly` needs to provide the original md5 of dhe dll - Add dhe dll, dhao files and `{manifest}` files to the hot update resource management system +If you use a workflow without validation, do the following: + +- Add dhe dll and dhao files to the hot update resource management system + If you want to carry the dhe dll and dhao files of the first package with the package, please export the project first, then follow the above steps to generate the dhe dll and dhao files, and then add them to the exported project. @@ -180,11 +220,17 @@ If you package using the **development build option**, be sure to use `HybridCLR #### Build the main package -- Use the trimmed AOT dll generated after building as the dhe dll of the first package (without any changes) -- Use `HybridCLR.Editor.DHE.BuildUtils.EncryptDllAndGenerateUnchangedDHAODatas` to generate the dhao file of the first package and addEncrypted dhe dll file -- Add dhe dll and dhao files to the hot update resource management system +- Use the cropped AOT dll generated after the build as the original dhe dll +- Use `HybridCLR.Editor.DHE.BuildUtils.EncryptDllAndGenerateUnchangedDHAODatas` to generate the dhao file of the first package and the encrypted dhe dll file -If you want to carry the dhe dll and dhao files of the first package with the package, please export the project first, then follow the above steps to generate the dhe dll and dhao files, and then add them to the exported project. +If using a checked workflow, do the following: + +- Generate a `{manifest}` manifest file for dhe dll that contains at least assemblyName, md5 (it is up to the developer to decide how to implement it), because `RuntimeApi.LoadDifferentialHybridAssembly` needs to provide the original md5 of dhe dll +- Add the encrypted dhe dll, dhao files and `{manifest}` files to the hot update resource management system + +If you use a workflow without validation, do the following: + +- Add the encrypted dhe dll and dhao files to the hot update resource management system #### Hot update diff --git a/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstart.md b/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstart.md deleted file mode 100644 index 58d773a7b..000000000 --- a/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstart.md +++ /dev/null @@ -1,468 +0,0 @@ -# Get started quickly - -This tutorial guides you to experience HybridCLR hot update from an empty project. For the sake of simplicity, only the case where the BuildTarget is the **Windows** or **MacOS** Standalone platform is demonstrated. -Please run the hot update process correctly on the Standalone platform before trying the hot update on the Android and iOS platforms. Their processes are very similar. - -The difficulty of using the flagship version is similar to that of the community version, and most of the principles are the same. It is recommended to familiarize yourself with the community version before trying the flagship version. - -## Experience goals - -- Create hot update assembly -- Load the hot update assembly, execute the hot update code in it, and print `Hello, HybridCLR` -- Modify the hot update code to print `Hello, World` - -## Prepare environment - -### Install Unity - -- Install any version 2019.4.x, 2020.3.x, 2021.3.x, 2022.3.x. Some versions have special installation requirements, see [Install hybridclr](../../basic/install.md) -- Depending on the operating system you are using, when selecting modules during the installation process, you must select `Windows Build Support(IL2CPP)` or `Mac Build Support(IL2CPP)` - -![select il2cpp modules](/img/hybridclr/select_il2cpp_modules.jpg) - -### Install IDE and related compilation environment - -- Windows - - `visual studio 2019` or higher version needs to be installed under Win. The installation must include at least the `Game development using Unity` and `Game development using C++` components. - - install git --Mac - - Requires MacOS version >= 12, xcode version >= 13, for example `xcode 13.4.1, macos 12.4` - - install git - -## Initialize Unity hot update project - -The process of constructing a hot update project from scratch is lengthy. The code involved in the following steps can refer to the dhe_demo project, whose warehouse address is [github](https://github.com/focus-creative-games/dhe_demo). - -### Create project - -Create an empty Unity project. - -### Create `ConsoleToScreen.cs` script - -This script has no direct effect on demonstrating hot updates. It can print logs to the screen to facilitate locating errors. - -Create the `Assets/ConsoleToScreen.cs` script class with the following code: - -```csharp -using System; -using System.Collections; -using System.Collections.Generic; -using UnityEngine; - -public class ConsoleToScreen : MonoBehaviour -{ - const int maxLines = 50; - const int maxLineLength = 120; - private string _logStr = ""; - - private readonly List _lines = new List(); - - public int fontSize = 15; - - void OnEnable() { Application.logMessageReceived += Log; } - void OnDisable() { Application.logMessageReceived -= Log; } - - public void Log(string logString, string stackTrace, LogType type) - { - foreach (var line in logString.Split('\n')) - { - if (line.Length <= maxLineLength) - { - _lines.Add(line); - continue; - } - var lineCount = line.Length / maxLineLength + 1; - for (int i = 0; i < lineCount; i++) - { - if ((i + 1) * maxLineLength <= line.Length) - { - _lines.Add(line.Substring(i * maxLineLength, maxLineLength)); - } - else - { - _lines.Add(line.Substring(i * maxLineLength, line.Length - i * maxLineLength)); - } - } - } - if (_lines.Count > maxLines) - { - _lines.RemoveRange(0, _lines.Count - maxLines); - } - _logStr = string.Join("\n", _lines); - } - - voidOnGUI() - { - GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, - new Vector3(Screen.width / 1200.0f, Screen.height / 800.0f, 1.0f)); - GUI.Label(new Rect(10, 10, 800, 370), _logStr, new GUIStyle() { fontSize = Math.Max(10, fontSize) }); - } -} - - -``` - -### Create the main scene - -- Create default initial scene main.scene -- Create an empty GameObject in the scene and hang ConsoleToScreen on it -- Add the main scene to the packaged scene list in `Build Settings` - -### Create HotUpdate hot update module - -- Create `Assets/HotUpdate` directory -- Right-click `Create/Assembly Definition` in the directory and create an assembly module named `HotUpdate` - -## Install and configure HybridCLR - -### Install - -- Unzip hybridclr_unity.zip, place it in the project Packages directory, and rename it com.code-philosophy.hybridclr -- Unzip the corresponding `libil2cpp-{version}.7z` according to your unity version -- Open `HybridCLR/Installer`, enable the `Copy libil2cpp from local` option, select the libil2cpp directory you just decompressed, and install it. -- Depending on your Unity version: - - If version >= 2020, replace the `ModifiedDlls\{verions}\Unity.IL2CPP.dll` file with `{proj}\HybridCLRData\LocalIl2CppData-WindowsEditor\il2cpp\build\deploy\netcoreapp3.1\Unity.IL2CPP.dll` (Unity 2020) or `{proj}\HybridCLRData\LocalIl2CppData-WindowsEditor\il2cpp\build\deploy\Unity.IL2CPP.dll` (Unity 2021+). If there is no file corresponding to your version, contact us to make one. - - If the version is 2019, no operation is required because it has been automatically copied during the Install process - -![installer](/img/hybridclr/ultimate-installer.jpg) - -### Configure HybridCLR - -- Open menu `HybridCLR/Settings` -- Add `HotUpdate` assembly to `differentialHybridAssemblies` list - -![settings](/img/hybridclr/ultimate-hybridclr-settings.jpg) - -### Configure PlayerSettings - -- `Scripting Backend` switched to `IL2CPP` -- `Api Compatability Level` switched to `.Net 4.x` (Unity 2019-2020) or `.Net Framework` (Unity 2021+) - -![player settings](/img/hybridclr/ultimate-project-settings.jpg) - -## Create Editor script - -Create the BuildTools.cs file in the `Assets/Editor` directory with the following content: - -```csharp - -using HybridCLR.Editor; -using HybridCLR.Editor.DHE; -using HybridCLR.Runtime; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using UnityEditor; -using UnityEngine; - -public static class BuildTools -{ - public const string BackupAOTDllDir = "HybridCLRData/BackupAOT"; - - public const string EncryptedDllDir = "HybridCLRData/EncryptedDll"; - - public const string DhaoDir = "HybridCLRData/Dhao"; - - public const string ManifestFile = "manifest.txt"; - - - /// - /// Back up the cropped AOT dll generated when building the main package - /// - [MenuItem("BuildTools/BackupAOTDll")] - public static void BackupAOTDllFromAssemblyPostStrippedDir() - { - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - var backupDir = $"{BackupAOTDllDir}/{target}"; - System.IO.Directory.CreateDirectory(backupDir); - var dlls = System.IO.Directory.GetFiles(SettingsUtil.GetAssembliesPostIl2CppStripDir(target)); - foreach (var dll in dlls) - { - var fileName = System.IO.Path.GetFileName(dll); - string dstFile = $"{BackupAOTDllDir}/{target}/{fileName}"; - System.IO.File.Copy(dll, dstFile, true); - Debug.Log($"BackupAOTDllFromAssemblyPostStrippedDir: {dll} -> {dstFile}"); - } - } - - /// - /// 创建dhe manifest文件,格式为每行一个 'dll名,原始dll的md5' - /// - /// - [MenuItem("BuildTools/CreateManifestAtBackupDir")] - public static void CreateManifest() - { - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - string backupDir = $"{BackupAOTDllDir}/{target}"; - CreateManifest(backupDir); - } - - public static void CreateManifest(string outputDir) - { - Directory.CreateDirectory(outputDir); - var lines = new List(); - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - string backupDir = $"{BackupAOTDllDir}/{target}"; - foreach (string dheDll in SettingsUtil.DifferentialHybridAssemblyNames) - { - string originalDll = $"{backupDir}/{dheDll}.dll"; - string originalDllMd5 = AssemblyOptionDataGenerator.CreateMD5Hash(File.ReadAllBytes(originalDll)); - lines.Add($"{dheDll},{originalDllMd5}"); - } - string manifestFile = $"{outputDir}/{ManifestFile}"; - File.WriteAllBytes(manifestFile, System.Text.Encoding.UTF8.GetBytes(string.Join("\n", lines))); - Debug.Log($"CreateManifest: {manifestFile}"); - } - - /// - /// 生成首包的没有任何代码改动对应的dhao数据 - /// - [MenuItem("BuildTools/GenerateUnchangedDHAODatas")] - public static void GenerateUnchangedDHAODatas() - { - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - string backupDir = $"{BackupAOTDllDir}/{target}"; - string dhaoDir = $"{DhaoDir}/{target}"; - BuildUtils.GenerateUnchangedDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, dhaoDir); - } - - /// - /// 生成热更包的dhao数据 - /// - [MenuItem("BuildTools/GenerateDHAODatas")] - public static void GenerateDHAODatas() - { - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - string backupDir = $"{BackupAOTDllDir}/{target}"; - string dhaoDir = $"{DhaoDir}/{target}"; - string currentDllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); - BuildUtils.GenerateDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, currentDllDir, dhaoDir); - } - - /// - /// 生成首包的加密dll和没有任何代码改动对应的dhao数据 - /// - [MenuItem("BuildTools/GenerateUnchangedEncryptedDllAndDhaoDatas")] - public static void GenerateUnchangedEncryptedDllAndDhaoDatas() - { - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - string backupDir = $"{BackupAOTDllDir}/{target}"; - string dhaoDir = $"{DhaoDir}/{target}"; - string encryptedDllDir = $"{EncrypedDllDir}/{target}"; - BuildUtils.EncryptDllAndGenerateUnchangedDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, encryptedDllDir, dhaoDir); - } - - - /// - /// 生成热更包的加密dll和dhao数据 - /// - [MenuItem("BuildTools/GenerateEncryptedDllAndDhaoDatas")] - public static void GenerateEncryptedDllAndDhaoDatas() - { - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - string backupDir = $"{BackupAOTDllDir}/{target}"; - string dhaoDir = $"{DhaoDir}/{target}"; - string currentDllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); - string encryptedDllDir = $"{EncrypedDllDir}/{target}"; - BuildUtils.EncryptDllAndGenerateDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, currentDllDir, encryptedDllDir, dhaoDir); - } - - /// - /// 复制没有改动的首包dll和dhao文件到StreamingAssets - /// - [MenuItem("BuildTools/CopyUnchangedDllAndDhaoFileAndManifestToStreamingAssets")] - public static void CopyUnchangedDllAndDhaoFileToStreamingAssets() - { - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - string streamingAssetsDir = Application.streamingAssetsPath; - Directory.CreateDirectory(streamingAssetsDir); - - string manifestFile = $"{BackupAOTDllDir}/{target}/{ManifestFile}"; - string dstManifestFile = $"{streamingAssetsDir}/{ManifestFile}"; - System.IO.File.Copy(manifestFile, dstManifestFile, true); - Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {manifestFile} -> {dstManifestFile}"); - - string dllDir = $"{BackupAOTDllDir}/{target}"; - string dhaoDir = $"{DhaoDir}/{target}"; - foreach (var dll in SettingsUtil.DifferentialHybridAssemblyNames) - { - string srcFile = $"{dllDir}/{dll}.dll"; - string dstFile = $"{streamingAssetsDir}/{dll}.dll.bytes"; - System.IO.File.Copy(srcFile, dstFile, true);Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {srcFile} -> {dstFile}"); - string dhaoFile = $"{dhaoDir}/{dll}.dhao.bytes"; - dstFile = $"{streamingAssetsDir}/{dll}.dhao.bytes"; - System.IO.File.Copy(dhaoFile, dstFile, true); - Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {dhaoFile} -> {dstFile}"); - } - } - - /// - /// 复制热更新dll和dhao文件到StreamingAssets - /// - [MenuItem("BuildTools/CopyDllAndDhaoFileToStreamingAssets")] - public static void CopyDllAndDhaoFileToStreamingAssets() - { - BuildTarget target = EditorUserBuildSettings.activeBuildTarget; - string streamingAssetsDir = Application.streamingAssetsPath; - Directory.CreateDirectory(streamingAssetsDir); - - string dllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); - string dhaoDir = $"{DhaoDir}/{target}"; - foreach (var dll in SettingsUtil.DifferentialHybridAssemblyNames) - { - string srcFile = $"{dllDir}/{dll}.dll"; - string dstFile = $"{streamingAssetsDir}/{dll}.dll.bytes"; - System.IO.File.Copy(srcFile, dstFile, true); - Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {srcFile} -> {dstFile}"); - string dhaoFile = $"{dhaoDir}/{dll}.dhao.bytes"; - dstFile = $"{streamingAssetsDir}/{dll}.dhao.bytes"; - System.IO.File.Copy(dhaoFile, dstFile, true); - Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {dhaoFile} -> {dstFile}"); - } - } -} - - -``` - -## 创建热更新脚本 - -创建 `Assets/HotUpdate/Hello.cs` 文件,代码内容如下 - -```csharp -using System.Collections; -using UnityEngine; - -public class Hello -{ - public static void Run() - { - Debug.Log("Hello, HybridCLR"); - } -} -``` - -## 加载热更新程序集 - -为了简化演示,我们不通过http服务器下载HotUpdate.dll,而是直接将HotUpdate.dll放到StreamingAssets目录下。 - -HybridCLR是原生运行时实现,因此调用`Assembly Assembly.Load(byte[])`即可加载热更新程序集。 - -创建`Assets/LoadDll.cs`脚本,然后**在main场景中创建一个GameObject对象,挂载LoadDll脚本**。 - -```csharp -using HybridCLR; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; -using UnityEngine; -using UnityEngine.Networking; - -public class LoadDll : MonoBehaviour -{ - - void Start() - { - // Editor环境下,HotUpdate.dll.bytes已经被自动加载,不需要加载,重复加载反而会出问题。 -#if !UNITY_EDITOR - var manifests = LoadManifest($"{Application.streamingAssetsPath}/manifest.txt"); - Assembly hotUpdateAss = LoadDifferentialHybridAssembly(manifests["HotUpdate"], "HotUpdate"); -#else - // Editor下无需加载,直接查找获得HotUpdate程序集 - Assembly hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate"); -#endif - Type helloType = hotUpdateAss.GetType("Hello"); - MethodInfo runMethod = helloType.GetMethod("Run"); - runMethod.Invoke(null, null); - } - - class Manifest - { - public string AssemblyName { get; set; } - - public string OriginalDllMd5 { get; set; } - } - - private Dictionary LoadManifest(string manifestFile) - { - var manifest = new Dictionary(); - var lines = File.ReadAllLines(manifestFile, Encoding.UTF8); - foreach (var line in lines) - { - string[] args = line.Split(","); - if (args.Length != 2) - { - Debug.LogError($"manifest file format error, line={line}"); - return null; - } - manifest.Add(args[0], new Manifest() - { - AssemblyName = args[0], - OriginalDllMd5 = args[1], - }); - } - return manifest; - } - - - public static string CreateMD5Hash(byte[] bytes) - { - return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(bytes)).Replace("-", "").ToUpperInvariant(); - } - - private Assembly LoadDifferentialHybridAssembly(Manifest manifest, string assName) - { - byte[] dllBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{assName}.dll.bytes"); - byte[] dhaoBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{assName}.dhao.bytes"); - string currentDllMd5 = CreateMD5Hash(dllBytes); - LoadImageErrorCode err = RuntimeApi.LoadDifferentialHybridAssembly(dllBytes, dhaoBytes, manifest.OriginalDllMd5, currentDllMd5); - if (err == LoadImageErrorCode.OK) - { - Debug.Log($"LoadDifferentialHybridAssembly {assName} OK"); - return System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == assName); - } - else - { - Debug.LogError($"LoadDifferentialHybridAssembly {assName} failed, err={err}"); - return null; - } - } -} - -``` - - -至此,完成整个热更新工程的创建工作!!! - -## Editor中试运行 - -运行main场景,屏幕上会显示 'Hello,HybridCLR',表示代码工作正常。 - -## 打包运行 - -- 运行菜单 `HybridCLR/Generate/All` performs necessary build operations. **This step cannot be missed**!!! -- Open the `Build Settings` dialog box, click `Build`, select the output directory `{build}`, and execute the build -- Run `BuildTools/BackupAOTDll` to back up the trimmed dhe dll. In practice, these dlls should be added to version management for later generation of dhao files. These files will not be modified again. -- Run `BuildTools/CreateManifestAtBackupDir` to generate the manifest file of the original dhe dll. In practice, this manifest file should be added to version management and will not be modified again. -- Run `BuildTools/GenerateUnchangedDHAODatas` to generate the dhao file of the first package -- Run `BuildTools/CopyUnchangedDllAndDhaoFileAndManifestToStreamingAssets` to copy the first package dhe assembly, dhao file, and manifest file to StreamingAssets -- Copy the `Assets/StreamingAssets` directory to `{build}\dhe_demo2_Data\StreamingAssets` -- Run `{build}/Xxx.exe`, and the screen will display `Hello,HybridCLR`, indicating that the hot update code has been successfully executed! - -## Test hot update - -- Modify the `Debug.Log("Hello, HybridCLR");` code in the Run function of `Assets/HotUpdate/Hello.cs` to `Debug.Log("Hello, World");`. -- Run `HybridCLR/CompileDll/ActiveBulidTarget` to generate hot update dll -- Run `BuildTools/GenerateDHAODatas` to generate dhao files -- Run `BuildTools/CopyDllAndDhaoFileToStreamingAssets` to copy the hot update dll and dhao files to the StreamingAssets directory -- Copy the `Assets/StreamingAssets` directory to `{build}\dhe_demo2_Data\StreamingAssets` -- Rerun the program and you will find `Hello, World` displayed on the screen, indicating that the hot update code has taken effect! - - -This completes the hot update experience! ! ! diff --git a/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstartchecked.md b/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstartchecked.md new file mode 100644 index 000000000..86668631c --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstartchecked.md @@ -0,0 +1,478 @@ +# Get started quickly (checked workflow) + +This tutorial guides you to experience HybridCLR hot update from an empty project. For the sake of simplicity, only the case where the BuildTarget is the **Windows** or **MacOS** Standalone platform is demonstrated. +Please run the hot update process correctly on the Standalone platform before trying the hot update on the Android and iOS platforms. Their processes are very similar. + +The difficulty of using the flagship version is similar to that of the community version, and most of the principles are the same. It is recommended to familiarize yourself with the community version before trying the flagship version. + +Since version v5.0.0, both the `RuntimeApi.LoadDifferentialHybridAssembly` workflow with verification and the `RuntimeApi.LoadDifferentialHybridAssemblyUnchecked` workflow without verification are supported. +This document introduces the workflow with validation. + +:::tip + +In practice, the workflow without verification will be much simpler. There is no need to pass the originalDllMd5 and currentDllMd5 parameters, so the process of saving or calculating dll md5 in the workflow is omitted. +However, developers are required to ensure the consistency of aot dll, hot update dll, and dhao files. +It is recommended that beginners use the workflow with verification in demo projects. After becoming familiar with the workflow, they can use the workflow without verification in formal projects. + +::: + +## Experience goals + +- Create hot update assembly +- Load the hot update assembly, execute the hot update code in it, and print `Hello, HybridCLR` +- Modify the hot update code to print `Hello, World` + +## Prepare environment + +### Install Unity + +- Install any version 2019.4.x, 2020.3.x, 2021.3.x, 2022.3.x. Some versions have special installation requirements, see [Install hybridclr](../../basic/install.md) +- Depending on the operating system you are using, when selecting modules during the installation process, you must select `Windows Build Support(IL2CPP)` or `Mac Build Support(IL2CPP)` + +![select il2cpp modules](/img/hybridclr/select_il2cpp_modules.jpg) + +### Install IDE and related compilation environment + +- Windows + - `visual studio 2019` or higher version needs to be installed under Win. The installation must include at least the `Game development using Unity` and `Game development using C++` components. + - install git +-Mac + - Requires MacOS version >= 12, xcode version >= 13, for example `xcode 13.4.1, macos 12.4` + - install git + +## Initialize Unity hot update project + +The process of constructing a hot update project from scratch is lengthy. The code involved in the following steps can refer to the dhe_demo project, whose warehouse address is [github](https://github.com/focus-creative-games/dhe_demo). + +### Create project + +Create an empty Unity project. + +### Create `ConsoleToScreen.cs` script + +This script has no direct effect on demonstrating hot updates. It can print logs to the screen to facilitate locating errors. + +Create the `Assets/ConsoleToScreen.cs` script class with the following code: + +```csharp +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ConsoleToScreen : MonoBehaviour +{ + const int maxLines = 50; + const int maxLineLength = 120; + private string _logStr = ""; + + private readonly List _lines = new List(); + + public int fontSize = 15; + + void OnEnable() { Application.logMessageReceived += Log; } + void OnDisable() { Application.logMessageReceived -= Log; } + + public void Log(string logString, string stackTrace, LogType type) + { + foreach (var line in logString.Split('\n')) + { + if (line.Length <= maxLineLength) + { + _lines.Add(line); + continue; + } + var lineCount = line.Length / maxLineLength + 1; + for (int i = 0; i < lineCount; i++) + { + if ((i + 1) * maxLineLength <= line.Length) + { + _lines.Add(line.Substring(i * maxLineLength, maxLineLength)); + } + else + { + _lines.Add(line.Substring(i * maxLineLength, line.Length - i * maxLineLength)); + } + } + } + if (_lines.Count > maxLines) + { + _lines.RemoveRange(0, _lines.Count - maxLines); + } + _logStr = string.Join("\n", _lines); + } + + voidOnGUI() + { + GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, + new Vector3(Screen.width / 1200.0f, Screen.height / 800.0f, 1.0f)); + GUI.Label(new Rect(10, 10, 800, 370), _logStr, new GUIStyle() { fontSize = Math.Max(10, fontSize) }); + } +} + + +``` + +### Create the main scene + +- Create default initial scene main.scene +- Create an empty GameObject in the scene and hang ConsoleToScreen on it +- Add the main scene to the packaged scene list in `Build Settings` + +### Create HotUpdate hot update module + +- Create `Assets/HotUpdate` directory +- Right-click `Create/Assembly Definition` in the directory and create an assembly module named `HotUpdate` + +## Install and configure HybridCLR + +### Install + +- Unzip hybridclr_unity.zip, place it in the project Packages directory, and rename it com.code-philosophy.hybridclr +- Unzip the corresponding `libil2cpp-{version}.7z` according to your unity version +- Open `HybridCLR/Installer`, enable the `Copy libil2cpp from local` option, select the libil2cpp directory you just decompressed, and install it. +- Depending on your Unity version: + - If version >= 2020, replace the `ModifiedDlls\{verions}\Unity.IL2CPP.dll` file with `{proj}\HybridCLRData\LocalIl2CppData-WindowsEditor\il2cpp\build\deploy\netcoreapp3.1\Unity.IL2CPP.dll` (Unity 2020) or `{proj}\HybridCLRData\LocalIl2CppData-WindowsEditor\il2cpp\build\deploy\Unity.IL2CPP.dll` (Unity 2021+). If there is no file corresponding to your version, contact us to make one. + - If the version is 2019, no operation is required because it has been automatically copied during the Install process + +![installer](/img/hybridclr/ultimate-installer.jpg) + +### Configure HybridCLR + +- Open menu `HybridCLR/Settings` +- Add `HotUpdate` assembly to `differentialHybridAssemblies` list + +![settings](/img/hybridclr/ultimate-hybridclr-settings.jpg) + +### Configure PlayerSettings + +- `Scripting Backend` switched to `IL2CPP` +- `Api Compatability Level` switched to `.Net 4.x` (Unity 2019-2020) or `.Net Framework` (Unity 2021+) + +![player settings](/img/hybridclr/ultimate-project-settings.jpg) + +## Create Editor script + +Create the BuildTools.cs file in the `Assets/Editor` directory with the following content: + +```csharp + +using HybridCLR.Editor; +using HybridCLR.Editor.DHE; +using HybridCLR.Runtime; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEditor; +using UnityEngine; + +public static class BuildTools +{ + public const string BackupAOTDllDir = "HybridCLRData/BackupAOT"; + + public const string EncryptedDllDir = "HybridCLRData/EncryptedDll"; + + public const string DhaoDir = "HybridCLRData/Dhao"; + + public const string ManifestFile = "manifest.txt"; + + + /// + /// Back up the cropped AOT dll generated when building the main package + /// + [MenuItem("BuildTools/BackupAOTDll")] + public static void BackupAOTDllFromAssemblyPostStrippedDir() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + var backupDir = $"{BackupAOTDllDir}/{target}"; + System.IO.Directory.CreateDirectory(backupDir); + var dlls = System.IO.Directory.GetFiles(SettingsUtil.GetAssembliesPostIl2CppStripDir(target)); + foreach (var dll in dlls) + { + var fileName = System.IO.Path.GetFileName(dll); + string dstFile = $"{BackupAOTDllDir}/{target}/{fileName}"; + System.IO.File.Copy(dll, dstFile, true); + Debug.Log($"BackupAOTDllFromAssemblyPostStrippedDir: {dll} -> {dstFile}"); + } + } + + /// + /// Create a dhe manifest file, the format is one 'dll name, md5 of the original dll' per line + /// + /// + [MenuItem("BuildTools/CreateManifestAtBackupDir")] + public static void CreateManifest() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + CreateManifest(backupDir); + } + + public static void CreateManifest(string outputDir) + { + Directory.CreateDirectory(outputDir); + var lines = new List(); + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + foreach (string dheDll in SettingsUtil.DifferentialHybridAssemblyNames) + { + string originalDll = $"{backupDir}/{dheDll}.dll"; + string originalDllMd5 = AssemblyOptionDataGenerator.CreateMD5Hash(File.ReadAllBytes(originalDll)); + lines.Add($"{dheDll},{originalDllMd5}"); + } + string manifestFile = $"{outputDir}/{ManifestFile}"; + File.WriteAllBytes(manifestFile, System.Text.Encoding.UTF8.GetBytes(string.Join("\n", lines))); + Debug.Log($"CreateManifest: {manifestFile}"); + } + + /// + /// Generate the dhao data corresponding to the first package without any code changes + /// + [MenuItem("BuildTools/GenerateUnchangedDHAODatas")] + public static void GenerateUnchangedDHAODatas() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + BuildUtils.GenerateUnchangedDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, dhaoDir); + } + + /// + /// Generate dhao data of hot update package + /// + [MenuItem("BuildTools/GenerateDHAODatas")] + public static void GenerateDHAODatas() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + string currentDllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); + BuildUtils.GenerateDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, currentDllDir, dhaoDir); + } + + /// + /// Generate the encrypted dll of the first package and the corresponding dhao data without any code changes + /// + [MenuItem("BuildTools/GenerateUnchangedEncryptedDllAndDhaoDatas")] + public static void GenerateUnchangedEncryptedDllAndDhaoDatas() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + string encryptedDllDir = $"{EncryptedDllDir}/{target}"; + BuildUtils.EncryptDllAndGenerateUnchangedDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, encryptedDllDir, dhaoDir); + } + + + /// + /// Generate the encrypted dll and dhao data of the hot update package + /// + [MenuItem("BuildTools/GenerateEncryptedDllAndDhaoDatas")] + public static void GenerateEncryptedDllAndDhaoDatas() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + string currentDllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); + string encryptedDllDir = $"{EncryptedDllDir}/{target}"; + BuildUtils.EncryptDllAndGenerateDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, currentDllDir, encryptedDllDir, dhaoDir); + } + + /// + /// Copy the unmodified first package dll and dhao files to StreamingAssets + /// + [MenuItem("BuildTools/CopyUnchangedDllAndDhaoFileAndManifestToStreamingAssets")] + public static void CopyUnchangedDllAndDhaoFileToStreamingAssets() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string streamingAssetsDir = Application.streamingAssetsPath; + Directory.CreateDirectory(streamingAssetsDir); + + string manifestFile = $"{BackupAOTDllDir}/{target}/{ManifestFile}"; + string dstManifestFile = $"{streamingAssetsDir}/{ManifestFile}"; + System.IO.File.Copy(manifestFile, dstManifestFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {manifestFile} -> {dstManifestFile}"); + + string dllDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + foreach (var dll in SettingsUtil.DifferentialHybridAssemblyNames) + { + string srcFile = $"{dllDir}/{dll}.dll"; + string dstFile = $"{streamingAssetsDir}/{dll}.dll.bytes"; + System.IO.File.Copy(srcFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {srcFile} -> {dstFile}"); + string dhaoFile = $"{dhaoDir}/{dll}.dhao.bytes"; + dstFile = $"{streamingAssetsDir}/{dll}.dhao.bytes"; + System.IO.File.Copy(dhaoFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {dhaoFile} -> {dstFile}"); + } + } + + /// + /// Copy hot update dll and dhao files to StreamingAssets + /// + [MenuItem("BuildTools/CopyDllAndDhaoFileToStreamingAssets")] + public static void CopyDllAndDhaoFileToStreamingAssets() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string streamingAssetsDir = Application.streamingAssetsPath; + Directory.CreateDirectory(streamingAssetsDir); + + string dllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); + string dhaoDir = $"{DhaoDir}/{target}"; + foreach (var dll in SettingsUtil.DifferentialHybridAssemblyNames) + { + string srcFile = $"{dllDir}/{dll}.dll"; + string dstFile = $"{streamingAssetsDir}/{dll}.dll.bytes"; + System.IO.File.Copy(srcFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {srcFile} -> {dstFile}"); + string dhaoFile = $"{dhaoDir}/{dll}.dhao.bytes"; + dstFile = $"{streamingAssetsDir}/{dll}.dhao.bytes"; + System.IO.File.Copy(dhaoFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {dhaoFile} -> {dstFile}"); + } + } +} + + +``` + +## Create hot update script + +Create the `Assets/HotUpdate/Hello.cs` file with the following code content + +```csharp +using System.Collections; +using UnityEngine; + +public class Hello +{ + public static void Run() + { + Debug.Log("Hello, HybridCLR"); + } +} +``` + +## Load hot update assembly + +To simplify the demonstration, we do not download HotUpdate.dll through the http server, but directly place HotUpdate.dll in the StreamingAssets directory. + +Create the `Assets/LoadDll.cs` script, then **create a GameObject object in the main scene and mount the LoadDll script**. + + +```csharp +using HybridCLR; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Networking; + +public class LoadDll : MonoBehaviour +{ + + void Start() + { + // In the Editor environment, HotUpdate.dll.bytes has been automatically loaded and does not need to be loaded. Repeated loading will cause problems. +#if !UNITY_EDITOR + var manifests = LoadManifest($"{Application.streamingAssetsPath}/manifest.txt"); + Assembly hotUpdateAss = LoadDifferentialHybridAssembly(manifests["HotUpdate"], "HotUpdate"); +#else + // No need to load under Editor, directly find the HotUpdate assembly + Assembly hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate"); +#endif + Type helloType = hotUpdateAss.GetType("Hello"); + MethodInfo runMethod = helloType.GetMethod("Run"); + runMethod.Invoke(null, null); + } + + class Manifest + { + public string AssemblyName { get; set; } + + public string OriginalDllMd5 { get; set; } + } + + private Dictionary LoadManifest(string manifestFile) + { + var manifest = new Dictionary(); + var lines = File.ReadAllLines(manifestFile, Encoding.UTF8); + foreach (var line in lines) + { + string[] args = line.Split(","); + if (args.Length != 2) + { + Debug.LogError($"manifest file format error, line={line}"); + return null; + } + manifest.Add(args[0], new Manifest() + { + AssemblyName = args[0], + OriginalDllMd5 = args[1], + }); + } + return manifest; + } + + + public static string CreateMD5Hash(byte[] bytes) + { + return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(bytes)).Replace("-", "").ToUpperInvariant(); + } + + private Assembly LoadDifferentialHybridAssembly(Manifest manifest, string assName) + { + byte[] dllBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{assName}.dll.bytes"); + byte[] dhaoBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{assName}.dhao.bytes"); + string currentDllMd5 = CreateMD5Hash(dllBytes); + LoadImageErrorCode err = RuntimeApi.LoadDifferentialHybridAssembly(dllBytes, dhaoBytes, manifest.OriginalDllMd5, currentDllMd5); + if (err == LoadImageErrorCode.OK) + { + Debug.Log($"LoadDifferentialHybridAssembly {assName} OK"); + return System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == assName); + }else + { + Debug.LogError($"LoadDifferentialHybridAssembly {assName} failed, err={err}"); + return null; + } + } +} + +``` + + +At this point, the creation of the entire hot update project is completed! ! ! + +## Trial run in Editor + +Run the main scene and 'Hello,HybridCLR' will be displayed on the screen, indicating that the code is working properly. + +## Package and run + +- Run menu `HybridCLR/Generate/All` to perform necessary generation operations. **This step cannot be missed**!!! +- Open the `Build Settings` dialog box, click `Build`, select the output directory `{build}`, and execute the build +- Run `BuildTools/BackupAOTDll` to back up the trimmed dhe dll. In practice, these dlls should be added to version management for later generation of dhao files. These files will not be modified again. +- Run `BuildTools/CreateManifestAtBackupDir` to generate the manifest file of the original dhe dll. In practice, this manifest file should be added to version management and will not be modified again. +- Run `BuildTools/GenerateUnchangedDHAODatas` to generate the dhao file of the first package +- Run `BuildTools/CopyUnchangedDllAndDhaoFileAndManifestToStreamingAssets` to copy the first package dhe assembly, dhao file, and manifest file to StreamingAssets +- Copy the `Assets/StreamingAssets` directory to `{build}\dhe_demo2_Data\StreamingAssets` +- Run `{build}/Xxx.exe`, and the screen will display `Hello,HybridCLR`, indicating that the hot update code has been successfully executed! + +## Test hot update + +- Modify the `Debug.Log("Hello, HybridCLR");` code in the Run function of `Assets/HotUpdate/Hello.cs` to `Debug.Log("Hello, World");`. +- Run `HybridCLR/CompileDll/ActiveBulidTarget` to generate hot update dll +- Run `BuildTools/GenerateDHAODatas` to generate dhao files +- Run `BuildTools/CopyDllAndDhaoFileToStreamingAssets` to copy the hot update dll and dhao files to the StreamingAssets directory +- Copy the `Assets/StreamingAssets` directory to `{build}\dhe_demo2_Data\StreamingAssets` +- Rerun the program and you will find `Hello, World` displayed on the screen, indicating that the hot update code has taken effect! + + +This completes the hot update experience. diff --git a/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstartunchecked.md b/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstartunchecked.md new file mode 100644 index 000000000..488c2eb29 --- /dev/null +++ b/i18n/en/docusaurus-plugin-content-docs/current/business/ultimate/quickstartunchecked.md @@ -0,0 +1,376 @@ +# Get started quickly (unchecked workflow) + +This tutorial guides you to experience HybridCLR hot update from an empty project. For the sake of simplicity, only the case where the BuildTarget is the **Windows** or **MacOS** Standalone platform is demonstrated. +Please run the hot update process correctly on the Standalone platform before trying the hot update on the Android and iOS platforms. Their processes are very similar. + +The difficulty of using the flagship version is similar to that of the community version, and most of the principles are the same. It is recommended to familiarize yourself with the community version before trying the flagship version. + +Since version v5.0.0, both the `RuntimeApi.LoadDifferentialHybridAssembly` workflow with verification and the `RuntimeApi.LoadDifferentialHybridAssemblyUnchecked` workflow without verification are supported. +This document describes the workflow without validation. + +:::tip + +In practice, the workflow without verification will be much simpler. There is no need to pass the originalDllMd5 and currentDllMd5 parameters, so the process of saving or calculating dll md5 in the workflow is omitted. +However, developers are required to ensure the consistency of aot dll, hot update dll, and dhao files. +It is recommended that beginners use the workflow with verification in demo projects. After becoming familiar with the workflow, they can use the workflow without verification in formal projects. + +::: + +## Experience goals + +- Create hot update assembly +- Load the hot update assembly, execute the hot update code in it, and print `Hello, HybridCLR` +- Modify the hot update code to print `Hello, World` + +## Prepare environment + +### Install Unity + +- Install any version 2019.4.x, 2020.3.x, 2021.3.x, 2022.3.x. Some versions have special installation requirements, see [Install hybridclr](../../basic/install.md) +- Depending on the operating system you are using, when selecting modules during the installation process, you must select `Windows Build Support(IL2CPP)` or `Mac Build Support(IL2CPP)` + +![select il2cpp modules](/img/hybridclr/select_il2cpp_modules.jpg) + +### Install IDE and related compilation environment + +- Windows + - `visual studio 2019` or higher version needs to be installed under Win. The installation must include at least the `Game development using Unity` and `Game development using C++` components. + - install git +- Mac + - Requires MacOS version >= 12, xcode version >= 13, for example `xcode 13.4.1, macos 12.4` + - install git + +## Initialize Unity hot update project + +The process of constructing a hot update project from scratch is lengthy. The code involved in the following steps can refer to the dhe_demo project, whose warehouse address is [github](https://github.com/focus-creative-games/dhe_demo). + +### Create project + +Create an empty Unity project. + +### Create `ConsoleToScreen.cs` script + +This script has no direct effect on demonstrating hot updates. It can print logs to the screen to facilitate locating errors. + +Create the `Assets/ConsoleToScreen.cs` script class with the following code: + +```csharp +using System; +using System.Collections; +using System.Collections.Generic; +using UnityEngine; + +public class ConsoleToScreen : MonoBehaviour +{ + const int maxLines = 50; + const int maxLineLength = 120; + private string _logStr = ""; + + private readonly List _lines = new List(); + + public int fontSize = 15; + + void OnEnable() { Application.logMessageReceived += Log; } + void OnDisable() { Application.logMessageReceived -= Log; } + + public void Log(string logString, string stackTrace, LogType type) + { + foreach (var line in logString.Split('\n')) + { + if (line.Length <= maxLineLength) + { + _lines.Add(line); + continue; + } + var lineCount = line.Length / maxLineLength + 1; + for (int i = 0; i < lineCount; i++) + { + if ((i + 1) * maxLineLength <= line.Length) + { + _lines.Add(line.Substring(i * maxLineLength, maxLineLength)); + } + else + { + _lines.Add(line.Substring(i * maxLineLength, line.Length - i * maxLineLength)); + } + } + } + if (_lines.Count > maxLines) + { + _lines.RemoveRange(0, _lines.Count - maxLines); + } + _logStr = string.Join("\n", _lines); + } + + voidOnGUI() + { + GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, + new Vector3(Screen.width / 1200.0f, Screen.height / 800.0f, 1.0f)); + GUI.Label(new Rect(10, 10, 800, 370), _logStr, new GUIStyle() { fontSize = Math.Max(10, fontSize) }); + } +} + + +``` + +### Create the main scene + +- Create default initial scene main.scene +- Create an empty GameObject in the scene and hang ConsoleToScreen on it +- Add the main scene to the packaged scene list in `Build Settings` + +### Create HotUpdate hot update module + +- Create `Assets/HotUpdate` directory +- Right-click `Create/Assembly Definition` in the directory and create an assembly module named `HotUpdate` + +## Install and configure HybridCLR + +### Install + +- Unzip hybridclr_unity.zip, place it in the project Packages directory, and rename it com.code-philosophy.hybridclr +- Unzip the corresponding `libil2cpp-{version}.7z` according to your unity version +- Open `HybridCLR/Installer`, enable the `Copy libil2cpp from local` option, select the libil2cpp directory you just decompressed, and install it. +- Depending on your Unity version: + - If version >= 2020, replace the `ModifiedDlls\{verions}\Unity.IL2CPP.dll` file with `{proj}\HybridCLRData\LocalIl2CppData-WindowsEditor\il2cpp\build\deploy\netcoreapp3.1\Unity.IL2CPP.dll` (Unity 2020) or `{proj}\HybridCLRData\LocalIl2CppData-WindowsEditor\il2cpp\build\deploy\Unity.IL2CPP.dll` (Unity 2021+). If there is no file corresponding to your version, contact us to make one. + - If the version is 2019, no operation is required because it has been automatically copied during the Install process + +![installer](/img/hybridclr/ultimate-installer.jpg) + +### Configure HybridCLR + +- Open menu `HybridCLR/Settings` +- Add `HotUpdate` assembly to `differentialHybridAssemblies` list + +![settings](/img/hybridclr/ultimate-hybridclr-settings.jpg) + +### Configure PlayerSettings + +- `Scripting Backend` switched to `IL2CPP` +- `Api Compatability Level` switched to `.Net 4.x` (Unity 2019-2020) or `.Net Framework` (Unity 2021+) + +![player settings](/img/hybridclr/ultimate-project-settings.jpg) + +## Create Editor script + +Create the BuildTools.cs file in the `Assets/Editor` directory with the following content: + +```csharp + +using HybridCLR.Editor; +using HybridCLR.Editor.DHE; +using HybridCLR.Runtime; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEditor; +using UnityEngine; + +public static class BuildTools +{ + public const string BackupAOTDllDir = "HybridCLRData/BackupAOT"; + + public const string DhaoDir = "HybridCLRData/Dhao"; + + + /// + /// Back up the cropped AOT dll generated when building the main package + /// + [MenuItem("BuildTools/BackupAOTDll")] + public static void BackupAOTDllFromAssemblyPostStrippedDir() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + var backupDir = $"{BackupAOTDllDir}/{target}"; + System.IO.Directory.CreateDirectory(backupDir); + var dlls = System.IO.Directory.GetFiles(SettingsUtil.GetAssembliesPostIl2CppStripDir(target)); + foreach (var dll in dlls) + { + var fileName = System.IO.Path.GetFileName(dll); + string dstFile = $"{BackupAOTDllDir}/{target}/{fileName}"; + System.IO.File.Copy(dll, dstFile, true); + Debug.Log($"BackupAOTDllFromAssemblyPostStrippedDir: {dll} -> {dstFile}"); + } + } + + /// + /// Generate the dhao data corresponding to the first package without any code changes + /// + [MenuItem("BuildTools/GenerateUnchangedDHAODatas")] + public static void GenerateUnchangedDHAODatas() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + BuildUtils.GenerateUnchangedDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, dhaoDir); + } + + /// + /// Generate dhao data of hot update package + /// + [MenuItem("BuildTools/GenerateDHAODatas")] + public static void GenerateDHAODatas() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string backupDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + string currentDllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); + BuildUtils.GenerateDHAODatas(SettingsUtil.DifferentialHybridAssemblyNames, backupDir, currentDllDir, dhaoDir); + } + + /// + /// Copy the unmodified first package dll and dhao files to StreamingAssets + /// + [MenuItem("BuildTools/CopyUnchangedDllAndDhaoFileToStreamingAssets")] + public static void CopyUnchangedDllAndDhaoFileToStreamingAssets() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string streamingAssetsDir = Application.streamingAssetsPath; + Directory.CreateDirectory(streamingAssetsDir); + + string dllDir = $"{BackupAOTDllDir}/{target}"; + string dhaoDir = $"{DhaoDir}/{target}"; + foreach (var dll in SettingsUtil.DifferentialHybridAssemblyNames) + { + string srcFile = $"{dllDir}/{dll}.dll"; + string dstFile = $"{streamingAssetsDir}/{dll}.dll.bytes"; + System.IO.File.Copy(srcFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {srcFile} -> {dstFile}"); + string dhaoFile = $"{dhaoDir}/{dll}.dhao.bytes"; + dstFile = $"{streamingAssetsDir}/{dll}.dhao.bytes"; + System.IO.File.Copy(dhaoFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {dhaoFile} -> {dstFile}"); + } + } + + /// + /// Copy hot update dll and dhao files to StreamingAssets + /// + [MenuItem("BuildTools/CopyDllAndDhaoFileToStreamingAssets")] + public static void CopyDllAndDhaoFileToStreamingAssets() + { + BuildTarget target = EditorUserBuildSettings.activeBuildTarget; + string streamingAssetsDir = Application.streamingAssetsPath; + Directory.CreateDirectory(streamingAssetsDir); + + string dllDir = SettingsUtil.GetHotUpdateDllsOutputDirByTarget(target); + string dhaoDir = $"{DhaoDir}/{target}"; + foreach (var dll in SettingsUtil.DifferentialHybridAssemblyNames) + { + string srcFile = $"{dllDir}/{dll}.dll"; + string dstFile = $"{streamingAssetsDir}/{dll}.dll.bytes"; + System.IO.File.Copy(srcFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {srcFile} -> {dstFile}"); + string dhaoFile = $"{dhaoDir}/{dll}.dhao.bytes"; + dstFile = $"{streamingAssetsDir}/{dll}.dhao.bytes"; + System.IO.File.Copy(dhaoFile, dstFile, true); + Debug.Log($"CopyUnchangedDllAndDhaoFileToStreamingAssets: {dhaoFile} -> {dstFile}"); + } + } +} + + +``` + +## Create hot update script + +Create the `Assets/HotUpdate/Hello.cs` file with the following code content + +```csharp +using System.Collections; +using UnityEngine; + +public class Hello +{ + public static void Run() + { + Debug.Log("Hello, HybridCLR"); + } +} +``` + +## Load hot update assembly + +To simplify the demonstration, we do not download HotUpdate.dll through the http server, but directly place HotUpdate.dll in the StreamingAssets directory. + +Create the `Assets/LoadDll.cs` script, then **create a GameObject object in the main scene and mount the LoadDll script**. + + +```csharp +using HybridCLR; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using UnityEngine.Networking; + +public class LoadDll : MonoBehaviour +{ + + void Start() + { + // In the Editor environment, HotUpdate.dll.bytes has been automatically loaded and does not need to be loaded. Repeated loading will cause problems. +#if !UNITY_EDITOR + Assembly hotUpdateAss = LoadDifferentialHybridAssembly("HotUpdate"); +#else + // No need to load under Editor, directly find the HotUpdate assembly + Assembly hotUpdateAss = System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == "HotUpdate"); +#endif + Type helloType = hotUpdateAss.GetType("Hello"); + MethodInfo runMethod = helloType.GetMethod("Run"); + runMethod.Invoke(null, null); + } + + private Assembly LoadDifferentialHybridAssembly(string assName) + { + byte[] dllBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{assName}.dll.bytes"); + byte[] dhaoBytes = File.ReadAllBytes($"{Application.streamingAssetsPath}/{assName}.dhao.bytes"); + LoadImageErrorCode err = RuntimeApi.LoadDifferentialHybridAssembly(dllBytes, dhaoBytes); + if (err == LoadImageErrorCode.OK) + { + Debug.Log($"LoadDifferentialHybridAssembly {assName} OK"); + return System.AppDomain.CurrentDomain.GetAssemblies().First(a => a.GetName().Name == assName); + } + else + { + Debug.LogError($"LoadDifferentialHybridAssembly {assName} failed, err={err}"); + return null; + } + } +} + +``` + + +At this point, the creation of the entire hot update project is completed! ! ! + +## Trial run in Editor + +Run the main scene and 'Hello,HybridCLR' will be displayed on the screen, indicating that the code is working properly. + +## Package and run + +- Run menu `HybridCLR/Generate/All` to perform necessary generation operations. **This step cannot be missed**!!! +- Open the `Build Settings` dialog box, click `Build`, select the output directory `{build}`, and execute the build +- Run `BuildTools/BackupAOTDll` to back up the trimmed dhe dll. In practice, these dlls should be added to version management for later generation of dhao files. These files will not be modified again. +- Run `BuildTools/GenerateUnchangedDHAODatas` to generate the dhao file of the first package +- Run `BuildTools/CopyUnchangedDllAndDhaoFileToStreamingAssets` to copy the first package dhe assembly and dhao files to StreamingAssets +- Copy the `Assets/StreamingAssets` directory to `{build}\dhe_demo2_Data\StreamingAssets` +- Run `{build}/Xxx.exe`, and the screen will display `Hello,HybridCLR`, indicating that the hot update code has been successfully executed! + +## Test hot update + +- Modify the `Debug.Log("Hello, HybridCLR");` code in the Run function of `Assets/HotUpdate/Hello.cs` to `Debug.Log("Hello, World");`. +- Run `HybridCLR/CompileDll/ActiveBulidTarget` to generate hot update dll +- Run `BuildTools/GenerateDHAODatas` to generate dhao files +- Run `BuildTools/CopyDllAndDhaoFileToStreamingAssets` to copy the hot update dll and dhao files to the StreamingAssets directory +- Copy the `Assets/StreamingAssets` directory to `{build}\dhe_demo2_Data\StreamingAssets` +- Rerun the program and you will find `Hello, World` displayed on the screen, indicating that the hot update code has taken effect! + + +This completes the hot update experience! diff --git a/sidebars.js b/sidebars.js index bc7d52296..6eb3663b0 100644 --- a/sidebars.js +++ b/sidebars.js @@ -99,7 +99,8 @@ const sidebars = { collapsed: true, items: [ 'business/ultimate/intro', - 'business/ultimate/quickstart', + 'business/ultimate/quickstartchecked', + 'business/ultimate/quickstartunchecked', 'business/ultimate/manual', 'business/ultimate/injectrules', 'business/ultimate/commonerrors',