找回密码
 立即注册
查看: 319|回复: 0

使用xlua 进行Unity3D 热更新-2

[复制链接]
发表于 2021-8-13 15:00 | 显示全部楼层 |阅读模式
一接触到新的东西,总想看看背后的原理是怎样的,xlua也不例外。于是试着写了一下,算是了解底层的实现原理,以后不用xlua也能有借鉴的地方。
xlua的热修复原理实际上是在 C# 编译成中间语言的时候,进行代码的插入这部分用到了 Mono.Ceil 库来操作,当然还有其他很多的库也可以实现。 因为是在IL的部分插入,因此直接支持IL2CPP
直接进入主题
已知有一个类
      1      
2      
3      
4      
5      
6      
7      
8      
9      
10      

publicclass InputTest      {

void Start      (){
        Hello      ();
}
privatevoid Hello      (){
        Debug.      Log("hello");
        Debug.      Log("666"):
}
}


这个类在被Unity调用的时候会输出 “Hello”
那么如果我们想修改Hello函数该怎么做呢

      1      
2      
3      
4      

      string injectPath       = @      "./Library\ScriptAssemblies\Assembly-CSharp.dll";
AssemblyDefinition assemblyDefinition       = null      ;
var readerParameters       =new ReaderParameters       { ReadSymbols       =true};
assemblyDefinition       = AssemblyDefinition.      ReadAssembly(injectPath, readerParameters      );


第一步 是要将当前代码的 Assembly 读出来, U3d有3个Assembly。 一个是项目代码叫 Assembly-CSharp.dll 一个是编辑器代码 Assembly-Editor-CSharp.dll.
还有一个是插件 Assembly-Plugin-CSharp.dll. 因为 InputTest是项目代码部分,所以读取 Assembly-CSharp.dll即可

读取成功后,所有的数据都在 assemblyDefinition 中,只需要遍历一下找到要修改的类即可
      1      
2      
3      
4      
5      
6      
7      
8      
9      
10      

       foreach       (Mono.      Cecil.      TypeDefinition item in assemblyDefinition.      MainModule.      Types){

if(item.      FullName=="InputTest"){
                    foreach       (MethodDefinition method in item.      Methods){

if(method.      Name.      Equals("Hello")){
}
}
}
}


第二步 通过遍历类型定义找到我们的类 “InputTest” 然后在 类定义中遍历所有的函数定义,找到我们要修改的 “Hello”函数
找到函数后,就可以正式做函数修改了。

      1      
2      
3      
4      
5      
6      
7      
8      
9      
10      
11      
12      
13      
14      
15      
16      
17      
18      
19      
20      
21      
22      
23      
24      
25      
26      

      var ins       = method.      Body.      Instructions.      First();
var worker       = method.      Body.      GetILProcessor();
var logRef       = assemblyDefinition.      MainModule.      Import(typeof      (Debug      ).      GetMethod("Log",       new Type      []{ typeof      (string      )}));

worker.      InsertBefore(ins, worker.      Create(OpCodes.      Ldstr,       "Fuck Off"));
worker.      InsertBefore(ins, worker.      Create(OpCodes.      Call, logRef      ));

worker.      InsertBefore(ins, worker.      Create(OpCodes.      Ldstr,       "Fuck On"));
worker.      InsertBefore(ins, worker.      Create(OpCodes.      Call, logRef      ));

Type type       = typeof      (InjectTest      );

if(null       != type      ){
     MethodInfo subMethod       = type.      GetMethod("SayFuck");

if(null       != subMethod      ){
          Debug.      Log("Find Method: "+ subMethod      );

          var sayRef       = assemblyDefinition.      MainModule.      Import(subMethod      );

          worker.      InsertBefore(ins, worker.      Create(OpCodes.      Call, sayRef      ));
}
}

var writerParameters       =new WriterParameters       { WriteSymbols       =true};
assemblyDefinition.      Write(injectPath,       new WriterParameters      ());


第三步 做了3件事情, 绑定了2个UnityEngine的Log函数,打印了 “Fuck Off”, “Fuck On” 之后再绑定一个类 “InjectTest”中的静态函数 SayFuck()
这样原本的 Hello()函数就会在 打印”Hello”之前先打印 “Fuck Off”, “Fuck On” 调用 InjectTest.SayFuck().

最后就是将执行的修改进行保存 assemblyDefinition.Write
最后的最后用C#反编译软件打开 Assembly-CSharp.dll 看看修改后的Hello()函数



可以看到已经成功的修改啦。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 09:07 , Processed in 0.090209 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表