InjectFix 热更新解决方案

简介

今天来谈一谈,项目种的客户端热更新解决方案。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.mfbz.cn/a/797655.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python爬虫:基础爬虫架构及爬取证券之星全站行情数据!

爬虫成长之路&#xff08;一&#xff09;里我们介绍了如何爬取证券之星网站上所有A股数据&#xff0c;主要涉及网页获取和页面解析的知识。爬虫成长之路&#xff08;二&#xff09;里我们介绍了如何获取代理IP并验证&#xff0c;涉及了多线程编程和数据存储的知识。此次我们将在…

深度学习LSTM之预测光伏发电

代码一&#xff1a;训练LSTM模型 代码逐段分析 import numpy as np import pandas as pd import tensorflow.keras as tk from tensorflow.keras import layers首先&#xff0c;导入了必要的库&#xff1a;numpy用于数值计算&#xff0c;pandas用于数据处理&#xff0c;tenso…

k8s record 20240710 监控

不是adaptor 是opetator 案例 监控有了&#xff0c;日志搜集呢&#xff1f; 一、kubelet 的小弟 kubelet — 负责维护容器的生命周期&#xff0c;节点和集群其他部分通信 cAdvisor 集成在 Kubernetes 的 kubelet 中&#xff0c;能够自动发现和监控集群中所有的容器。dockers…

尚硅谷Vue3入门到实战,最新版vue3+TypeScript前端开发教程

Vue3 编码规范 创建vue3工程 基于vite创建 快速上手 | Vue.js (vuejs.org) npm create vuelatest 在nodejs环境下运行进行创建 按提示进行创建 用vscode打开项目 安装依赖 源文件有src 内有main.ts App.vue 简单分析 编写src vue2语法在三中适用 vue2中的date metho…

java《ArrayList篇》--ArrayList全套知识点总结及其配套习题逐语句分析(附带全套源代码)

一、前言 来不及悼念字符串了&#xff0c;接下来登场的是集合&#xff0c;集合和数组的用法差不多&#xff0c;不同之处就在于存储的内容&#xff0c;数组是固定的长度的&#xff0c;集合的长度不固定。学习的过程中可以参照数组 今天已经是学习java的第八天了&#xff0c;接下…

vue3 vite+gojs 2.3.14 去除水印

引用vue2的做法&#xff1a;http://t.csdnimg.cn/Yrz8n 自定义vite插件&#xff0c;插件中apply 分两种模式&#xff0c;如果打包请选择build&#xff0c;记得强制刷新浏览器清缓存采能看到最新的gojs界面 export default function createGojsWaterMaker() {return {name:rem…

FPGA笔试

半加器和全加器的区别&#xff1a; 1、半加器不考虑输入的进位&#xff0c;称之为半加。 2、全加器反之&#xff0c;考虑进位。 SRAM/DRAM优缺点对比_sram和dram的主要区别及优缺点-CSDN博客 消除竞争冒险的方法 ①滤波电容&#xff1a;因为尖峰脉冲很窄&#xff0c;用很小的…

PyFluent入门之旅(5)后处理

接着PyFluent入门之旅&#xff08;4&#xff09;算例求解后我们已经完成了求解&#xff0c;并且保存了.dat的结果文件。 现在可以利用Fluent内置的后处理功能进行图像与数据曲线的输出。 1. 计算结果文件的读取 如果需要在计算完成后立即进行后处理&#xff0c;那么直接在求…

Nginx入门到精通六(高可用配置)

下面内容整理自bilibili-尚硅谷-Nginx青铜到王者视频教程 Nginx相关文章 Nginx入门到精通一&#xff08;基本概念介绍&#xff09;-CSDN博客 Nginx入门到精通二&#xff08;安装配置&#xff09;-CSDN博客 Nginx入门到精通三&#xff08;Nginx实例1&#xff1a;反向代理&a…

【Django+Vue3 线上教育平台项目实战】构建高效线上教育平台之首页模块

文章目录 前言一、导航功能实现a.效果图&#xff1a;b.后端代码c.前端代码 二、轮播图功能实现a.效果图b.后端代码c.前端代码 三、标签栏功能实现a.效果图b.后端代码c.前端代码 四、侧边栏功能实现1.整体效果图2.侧边栏功能实现a.效果图b.后端代码c.前端代码 3.侧边栏展示分类及…

springboot1——快速构建项目

需求 第一步&#xff1a;创建maven工程(非web项目) 第二步&#xff1a;导入起步依赖 点击&#xff1a; 下拉复制&#xff1a; 粘贴&#xff1a;&#xff01;&#xff01;这是springboot工程需要继承的父工程 下拉复制&#xff1a; 粘贴&#xff1a;&#xff01;&#xf…

android13 文件管理器无法安装apk 奔溃问题

总纲 android13 rom 开发总纲说明 目录 1.前言 2.我们简单写个apk测试下 3.排查客户apk 4.frameworks源码排查 5.编译验证 6.彩蛋 1.前言 客户提供的文件管理apk不能安装apk文件,一点击就奔溃。 2.我们简单写个apk测试下 private void installApk(File apkFile) {i…

将swagger注解导入apifox的IDEA配置

在使用IDEA开发中&#xff0c;经常需要将后端接口导出到Apifox&#xff0c;以便于测试。将swagger注解内容导出到Apifox中&#xff0c;需要进行以下设置: file->settting打开对话框&#xff0c;选择Other Settings -> Apifox Help&#xff0c;如下图&#xff1a; 2.选…

【JavaWeb程序设计】Servlet(二)

目录 一、改进上一篇博客Servlet&#xff08;一&#xff09;的第一题 1. 运行截图 2. 建表 3. 实体类 4. JSP页面 4.1 login.jsp 4.2 loginSuccess.jsp 4.3 loginFail.jsp 5. mybatis-config.xml 6. 工具类&#xff1a;创建SqlSessionFactory实例&#xff0c;进行 My…

Twelve Labs:专注视频理解,像人类一样理解视频内容

在当今数字化世界中&#xff0c;视频已成为人们获取信息和娱乐的主要方式之一。 AI视频生成领域的竞争也很激烈&#xff0c;Pika、Sora、Luma AI以及国内的可灵等&#xff0c;多模态、视频生成甚至也被视为大模型发展的某种必经之路。然而&#xff0c;与文本生成相比&#xff…

什么ISP?什么是IAP?

做单片机开发的工程师经常会听到两个词&#xff1a;ISP和IAP&#xff0c;但新手往往对这两个概念不是很清楚&#xff0c;今天就来和大家聊聊什么是ISP&#xff0c;什么是IAP&#xff1f; 一、ISP ISP的全称是&#xff1a;In System Programming&#xff0c;即在系统编程&…

【蓄势·致远】 同为科技(TOWE)2024年年中会议

2024年7月2日-8日&#xff0c;同为科技&#xff08;TOWE&#xff09;召开2024年年中工作会议。会议回顾上半年总体工作情况&#xff0c;分析研判发展形势&#xff0c;规划部署下半年工作。 为期一周的工作会议&#xff0c;由同为科技&#xff08;TOWE&#xff09;创始人、董事长…

MySQL的插入(DML)

1.给指定字段添加数据 这个就是&#xff0c;想插入所对应的字段&#xff0c;就插入所对应的数值。先把字段列出来&#xff0c;不一定是全部的字段&#xff0c; 然后插入想要的值&#xff0c;注意&#xff0c;只能插入一行。 INSERT INTO 表名 (字段1,字段2,.....) VALUES(值…

vue学习day08-v-model详解、sync修饰符、ref和$refs获取dom组件、Vue异步更新和$nextTick

25、v-model详解 &#xff08;1&#xff09;v-model原理 1&#xff09;原理: v-model本质上是一个语法糖&#xff0c;比如&#xff1a;在应用于输入框时&#xff0c;就是value属性与input事件的合写。 2&#xff09;作用 ①数据变&#xff0c;视图变 ②视图变&#xff0c…

网络协议 — Keepalived 高可用方案

目录 文章目录 目录Keepalived 是实现了 VRRP 协议的软件Keepalived 的软件架构VRRP StackCheckersKeepalived 的配置Global configurationvrrp_scriptVRRP Configurationvrrp synchroization groupvrrp instancevirtual ip addressesvirtual routesLVS Configurationvirtual_s…