使用xlua 进行Unity3D 热更新-2

一接触到新的东西,总想看看背后的原理是怎样的,xlua也不例外。于是试着写了一下,算是了解底层的实现原理,以后不用xlua也能有借鉴的地方。

xlua的热修复原理实际上是在 C# 编译成中间语言的时候,进行代码的插入这部分用到了 Mono.Ceil 库来操作,当然还有其他很多的库也可以实现。 因为是在IL的部分插入,因此直接支持IL2CPP

直接进入主题

已知有一个类

1
2
3
4
5
6
7
8
9
10
public  class InputTest {

     void Start ( ) {
        Hello ( ) ;
     }
     private  void 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()函数

扫描二维码关注公众号,回复: 1596230 查看本文章

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

猜你喜欢

转载自blog.csdn.net/weixin_39706943/article/details/80596995