InjectFix 热更新解决方案

news2024/11/15 6:46:17

简介

今天来谈一谈,项目种的客户端热更新解决方案。InjectFix是腾讯xlua团队出品的一种用于Unity中C#代码热更新热修复的解决方案。支持Unity全系列,全平台。与xlua的思路类似,InjectFix解决的痛点主要在于Unity中C#代码写的逻辑在发包之后无法更新,导致出现了严重的逻辑问题只能通过配置关闭功能或者利用资源更新来绕过bug这类问题。

相比较lua虚拟机热更的优点

相较于一般使用lua这种接入C#来进行热更新的如ulua之类的方案,InjectFix直接修改C#即可使用,老项目也可以使用,只要简单的接入相应的库,并依照补丁流程进行相应的操作即可。减少了额外学习一门语言的开销。

使用

官方链接:https://github.com/Tencent/InjectFix

1.注入(DLL插桩)

【InjectFix】-【Inject】来对我们的DLL进行自动插桩,需要在编辑器页面。
运行这个菜单工具后,这时IFix会根据我们提供的Config文件去给这些注册的类里面的每个方法插桩,它会直接修改 \Library\ScriptAssemblies\Assembly-CSharp.dll 这个文件,正常注入后即可得到一个拥有热更新能力的DLL文件。
所以我们需要在Editor目录下配置config文件添加需要热更的类。

原理如下。在.NET的CLR生成MSIL中间层语言时,在il代码中增加了一些跳转操作,如果检测到补丁就会执行相应的函数,本质上是修改了Unity生成的dll临时文件。
image.png
图1.1 注入后会修改MSIL代码
我们可以在il中清晰地看到这些逻辑。
image.png
如果IsPatche == false, 会跳转到IL_0021,否则顺序执行。

2.标注代码(制作补丁)

当有代码需要更新的时候,需要修改相应的代码。这里InjectFix主要提供了三种修改方式。这三种方式都是通过使用Attribute来标注被修改代码的途径来实现的。详细使用方式可以查看官方文档。这里说一下大概都是干什么的以及怎么用。

1.patch(用于修改一个函数)

比如

--- ImmortalGuideRootLogic.cs   (revision 323246)
+++ ImmortalGuideRootLogic.cs   (working copy)
@@ -81,6 +81,7 @@     
public GameObject m_Finished;      
public UIButton m_GoToGrowGuide;      
public UILabel m_TipsLabel;   //完成和等级不足公用一个label +    
[IFix.Patch]      
private void Start()     
{         
    if (m_DayItemList.Length != GlobeVar.IMMORTALGUIDE_TASKDAYCOUNT)
        @@ -87,7 +88,9 @@         
    { 
                    return;         
    } 
    - 
    +        //发消息请求仙人指路任务状态 
    +       CG_IMMORTALGUIDE_PROGRESS_REQ_PAK pak =
        new CG_IMMORTALGUIDE_PROGRESS_REQ_PAK();
    +       pak.SendPacket();         
            m_Instance = this;         
            m_LeftTime.text = "";         
    m_ProgressBonusPanel.SetActive(false);
//---------
}

这里代码的修改主要是在Start函数中增加了一些代码。增加了之后给函数标记Patch。这样之后生成Patch的时候,就能发现这个函数并生成相应的逻辑了。

2.Interpret(用于新增一个函数或者一个字段等)

[IFix.Patch]      
    void OnDestroy()      
  { 
+        OnDisable();
+  }
+ 
+    [IFix.Interpret] 
    +    void OnDisable() 
    +    {          m_tabBtnController.delTabWillChange -= TabChangeCheck;       
                      m_Instance = null;          
                      GameManager.PlayerDataPool.ChargeLTea.m_DelLTDrink -= UpDataChargeTeaRedDot;
                      @@ -72,19 +79,21 @@          
                    GameManager.PlayerDataPool.ChargeHTea.m_DelHTDrink -= UpDataChargeTeaRedDot;     
         }  
+    [IFix.Patch]     
    void UpdateRightBtnShow(TabIndex index, bool bShow)     
    { 
         if((int)index >= 0 && (int)index < (int)TabIndex.Count) 
      +  if((int)index >= 0 && (int)index < m_TabObject.Length)         
      {
          -           m_TabObject[(int)index].gameObject.SetActive(bShow);
          +            m_TabObject[(int)index].SetActive(bShow);        
      }     
} 

这里主要是想把原来用在Destroy的逻辑放到OnDisable中去。因为没有办法删除函数,所以直接删除函数中的逻辑,这里去掉了原来Destroy中的逻辑。然后新增了OnDisable函数用来相应相关的逻辑。
注意OnDisable在OnDestroy中进行了一次调用。这是因为在生成patch的时候会进行函数的裁剪。如果一个函数没有使用过的话,直接就被裁剪掉了。所以这里在别的函数用用一下,避免裁剪。

3.CustomBridge(用于告诉外界,虚拟机这里有一个类可以用)

因为本质上,InjectFix的Patch实现的所有的逻辑都是运行在一个用C#编写的虚拟机中
的,其实外界并不知道虚拟机中加载了什么样的逻辑。为了通知外界这里有一个逻辑可以让外界调用,需要用这个特性标注一下。
主要是为了以下这些使用情景。

  1. 修复代码赋值一个闭包到一个delegate变量;
  2. 修复代码的Unity协程用了yield return;
  3. 新增一个函数,赋值到一个delegate变量;
  4. 新增一个类,赋值到一个原生interface变量;
  5. 新增函数,用了yield return;

3.生成Patch

【InjectFix】-【Fix】生成补丁
按照上述方法标注了代码之后,就可以生成Patch了。即提取标注的代码,放到一个文件里。
在Unity的Menu中点击InjectFix->FixAll按钮执行对应的生成逻辑。
之后会在Client\IFixPatch路径下生成针对PC,ios和Android的三个patch包。再根据需要热更到的底包和资源版本号修改名字,提交上到CDN。

总结

IFix的原理主要包括两个部分:

  1. 自动插桩,首先在代码里面插桩,进入这些的函数的时候判断是否需要热更新,如果需要则直接跳转去执行热更新补丁中的IL指令。
  2. 生成补丁,将需要热更新的代码生成为IL指令。

周更

项目的热更新步骤是自动的。整体的过程是取到IFixPatch路径下的patch包。根据其名字取出这个patch对应的底包及资源路径。在执行热更新脚本的过程中,将这个patch进行改名,然后放到对应的资源更新的路径中,作为周更资源的一种进行更新。
底包在放出去之后,打开进行周更的时候,会下载patch包到机器的可读写路径中。加载完资源之后会进行尝试加载patch包的操作。加载了patch包之后,如果有被替换的逻辑,就会自动执行新的逻辑了。
这里可以很显然的看出,如果是资源更新完之前的逻辑出问题,热更新是无法解决的,所以这里需要额外注意

InjectFix 的缺点

确定注入的函数

要注入哪些函数其实也是根据配置生成的,详细可以看看官方文档。目前项目中的做法是注入了所有的Assembly-CSharp中的函数。所以在这之外的,比方说firstpass中的函数,就没有办法进行热更了。这一步会影响效率。

不能直接修改变量的值

整体的热更方案没有办法修改字段的值。所以如果修改一个变量的值,需要修改所有用到这个变量的逻辑。或者把变量改成以属性或者函数的方式来获得。

继承受限制

新增的类不可以继承外界的类。因为新的虚拟机没有实现这么多东西。

性能一般

补丁的逻辑性能很差,较外界的正常il2cpp(HybridCLR)差了大概3个数量级。所以频繁操作和复杂逻辑尽量不要热更,想办法绕过去。最好能不热更就不热更。

参考资料:https://www.jianshu.com/p/adf1cb2dbd3c


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1924610.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

复杂设备操作流程3D数字化全景展示好处多

传统的纸质说明书&#xff0c;尽管承载着产品的使用指南&#xff0c;但其图文平面的表现形式往往限制了表现力和说明力。对于简单产品&#xff0c;用户或许能摸索使用;但对于复杂产品&#xff0c;即使拥有详实的说明书&#xff0c;也可能因理解困难而导致使用障碍。现在&#x…

【工具使用】adb下载和配置

【工具使用】adb下载和配置 一&#xff0c;简介二&#xff0c;操作步骤2.1 Bing搜索adb2.2 下载adb工具2.3 添加路径到环境变量 三&#xff0c;效果验证 一&#xff0c;简介 本文主要介绍如何下载adb并安装使用&#xff0c;供参考。 此时打开cmd&#xff0c;输入adb 会提示&am…

<数据集>猫狗识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;3686张 标注数量(xml文件个数)&#xff1a;3686 标注数量(txt文件个数)&#xff1a;3686 标注类别数&#xff1a;2 标注类别名称&#xff1a;[cat, dog] 序号类别名称图片数框数1cat118811892dog24982498 使用标…

计算机网络复习笔记【面向考纲整理】

计算机网络复习笔记 一、计算机网络体系结构&#xff08;一&#xff09;计算机网络的概念、分类、组成与功能1.计算机网络的概念、组成与功能1.1计算机网络的概念1.2计算机网络的组成1.3计算机网络的功能 2.计算机网络的分类3.计算机网络的标准化工作及相关知识 &#xff08;二…

主机安全-进程、命令攻击与检测

目录 概述反弹shell原理nc/dev/xxx反弹shell下载不落地反弹Shell各种语言反弹shell linux提权sudosuid提权mysql提权 Dnslog参考 概述 本文更新通过在主机&#xff08;不含容器&#xff09;上直接执行命令或启动进程来攻击的场景。检测方面以字节跳动的开源HIDS elkeid举例。每…

E9.【C语言】练习:模拟用户登录界面,最多输入三次密码以及strcmp函数的讲解

思路&#xff1a;分两个环节 1.输入密码&#xff0c;存储在数组里 2.密码验证&#xff08;尝试次数不超过3次&#xff09; #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> int main() {char password[20] { 0 };int i 0;for ( i …

整洁架构SOLID-接口隔离原则(ISP)

文章目录 定义ISP与编程语言ISP与软件架构小结 定义 在上图中有多个用户需要操作OPS类。现在&#xff0c;我们假设这里的User1只需要使用op1,User2只需要使用op2,User3只需要使用op3。 在这种情况下&#xff0c;如果OPS类是用Java编程语言编写的&#xff0c;那么很明显&#x…

使用Gitee仓库镜像管理功能实现Gitee与Github 双向同步

进入你所需要同步的仓库&#xff0c;点击「管理」->「镜像仓库管理」&#xff0c;点击「添加镜像」选项&#xff1b; 如果你的Gitee账号还没有绑定过 GitHub 帐号&#xff0c;先根据弹窗的提示绑定 GitHub 帐号&#xff1b; 添加镜像时候&#xff0c;在「镜像方向」中选择…

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(三)-机上无线电接入节点无人机

引言 本文是3GPP TR 22.829 V17.1.0技术报告&#xff0c;专注于无人机&#xff08;UAV&#xff09;在3GPP系统中的增强支持。文章提出了多个无人机应用场景&#xff0c;分析了相应的能力要求&#xff0c;并建议了新的服务级别要求和关键性能指标&#xff08;KPIs&#xff09;。…

【微信小程序知识点】getApp()全局数据共享,页面间通信,组件间通信

getApp()-全局数据共享 在小程序中&#xff0c;可以通过getApp()方法获取到小程序全局唯一的App实例。因此在App()方法中添加全局共享的数据&#xff0c;方法&#xff0c;从而实现页面&#xff0c;组件的数据传值。 // app.js App({//全局共享的数据globalData: {token: &qu…

C语言——流程控制:if...else、switch...case

控制类语句&#xff1a; 逻辑运算符&#xff1a; 选择语句&#xff1a; if...else&#xff1a; if&#xff08;&#xff09;括号内的内容终究会被转换成0,1&#xff0c;满足的话即为1&#xff0c;不满足的话为0。因此要注意&#xff0c;&#xff08;&#xff09;括号内因为条件…

【数智化CIO展】沃太能源CIO陈丽:AI 浪潮下的中国企业数智化转型机遇与挑战...

陈丽 本文由沃太能源CIO陈丽投递并参与由数据猿联合上海大数据联盟共同推出的《2024中国数智化转型升级优秀CIO》榜单/奖项评选。 大数据产业创新服务媒体 ——聚焦数据 改变商业 在当今飞速发展的数字时代&#xff0c;中国企业正面临着前所未有的变革机遇和挑战。“中国企业数…

机器学习筑基篇,Jupyter Notebook 精简指南

[ 知识是人生的灯塔&#xff0c;只有不断学习&#xff0c;才能照亮前行的道路 ] 0x00 Jupyter Notebook 简明指南 描述&#xff1a;前面我们已经在机器学习工作站&#xff08;Ubuntu 24.04 Desktop Geforce RTX 4070Ti SUPER&#xff09;中安装 Anaconda 工具包&#xff0c;其…

Spark底层原理:案例解析(第34天)

系列文章目录 一、Spark架构设计概述 二、Spark核心组件 三、Spark架构设计举例分析 四、Job调度流程详解 五、Spark交互流程详解 文章目录 系列文章目录前言一、Spark架构设计概述1. 集群资源管理器&#xff08;Cluster Manager&#xff09;2. 工作节点&#xff08;Worker No…

Unity基础调色

叭叭叭 最近&#xff08;*这两天&#xff09;因为想做一些Unity的调色问题&#xff0c;尝试原文翻译一下&#xff0c;其实直接原文更好&#xff01;&#xff01; Color Grading 参考了&#xff0c;某大牛的翻译&#xff0c;实在忍不住了&#xff0c;我是不知道为什么能翻译成…

最新版智能修图-中文luminar ai 1.55(13797) 和 neo1.20,支持m芯片和intel芯片(绝对可用)

Luminar AI for macOS 完整版本 这个程序是第一个完全由人工智能驱动的图像编辑器。有了它&#xff0c;创建引人注目的照片是有趣的&#xff0c;令人惊讶的容易。它是一个独立的照片编辑器和macOS插件。 Luminar AI for macOS 轻轻地塑造和完善一个肖像打造富有表现力的眼睛…

Qt MV架构-委托类

一、基本概念 与MVC模式不同&#xff0c;MV视图架构中没有包含一个完全分离的组件来处理与用户的交互。 一般地&#xff0c;视图用来将模型中的数据显示给用户&#xff0c;也用来处理用户的输入。为了获得更高的灵活性&#xff0c;交互可以由委托来执行。 这些组件提供了输入…

excel、word、ppt 下载安装步骤整理

请按照我的步骤开始操作&#xff0c;注意以下截图红框标记处&#xff08;往往都是需要点击的地方&#xff09; 第一步&#xff1a;下载 首先进入office下载网址&#xff1a; otp.landian.vip 然后点击下载 拉到下方 下载站点&#xff08;这里根据自己的需要选择下载&#x…

IoTDB 集群高效管理:一键启停功能介绍

如何快速启动、停止 IoTDB 集群节点的功能详解&#xff01; 在部署 IoTDB 集群时&#xff0c;对于基础的单机模式&#xff0c;启动过程相对简单&#xff0c;仅需执行 start-standalone 脚本来启动 1 个 ConfigNode 节点和 1 个 DataNode 节点。然而&#xff0c;对于更高级的分布…

frp内网穿透ssh,tcp经过服务器慢速和p2p模式实现高速吃满上传带宽

ssh_server aliyun_server ssh_client 办公室 云服务器 家 在家里经过云服务器中转&#xff0c;很慢&#xff0c;但是很稳定 使用p2p穿透&#xff0c;速度可以直接拉满 ssh_server cc.ini # 连接服务器配置 [common] server_addr 1…