更新Xlua 到最新版本

This commit is contained in:
2024-09-04 02:07:48 +08:00
parent dba48de5cf
commit 5b79a2d4bb
227 changed files with 5594 additions and 878 deletions

View File

@ -1,32 +1,37 @@
## 简介
热补丁允许你使用 xLua 的代码逻辑, 替换掉原有的 C# 程序逻辑, 以实现热补丁.
## 使用方式
1、打开该特性
1. 打开该特性
添加HOTFIX_ENABLE宏在Unity3DFile->Build Setting->Scripting Define Symbols下添加。编辑器、各手机平台这个宏要分别设置如果是自动化打包要注意在代码里头用API设置的宏是不生效的需要在编辑器设置。
添加 `HOTFIX_ENABLE` 宏,(在 Unity3D 的 "File->Build Setting->Scripting Define Symbols" 下添加)。编辑器、各手机平台这个宏要分别设置!如果是自动化打包,要注意在代码里头用 API 设置的宏是不生效的,需要在编辑器设置。
建议平时开发业务代码不打开HOTFIX_ENABLE只在build手机版本或者要在编译器下开发补丁时打开HOTFIX_ENABLE
(建议平时开发业务代码不打开 `HOTFIX_ENABLE`,只在构建手机版本或者要在编译器下开发补丁时打开 `HOTFIX_ENABLE`
2、执行XLua/Generate Code菜单
2. 在菜单中找到 "XLua/Generate Code" 按钮并单击
3、注入,构建手机包这个步骤会在构建时自动进行,编辑器下开发补丁需要手动执行"XLua/Hotfix Inject In Editor"菜单。打印“hotfix inject finish!”或者“had injected!”才算成功,否则会打印错误信息。
3. 注入,构建手机包这个步骤会在构建时自动进行,编辑器下开发补丁需要手动执行 "XLua/Hotfix Inject In Editor" 菜单。打印 “hotfix inject finish!” 或者 “had injected!” 才算成功,否则会打印错误信息。
如果已经打印了“hotfix inject finish!”或者“had injected!”执行xlua.hotfix仍然报类似“xlua.access, no field __Hitfix0_Update”的错误要么是该类没配置到Hotfix列表要么是注入成功后又触发了编译覆盖了注入结果。
如果已经打印了 “hotfix inject finish!” 或者 “had injected!”,执行 `xlua.hotfix` 仍然报类似 “xlua.access, no field __Hitfix0_Update” 的错误,要么是该类没配置到 Hotfix 列表,要么是注入成功后,又触发了编译,覆盖了注入结果。
## 约束
## 局限性
不支持静态构造函数。
目前只支持Assets下代码的热补丁不支持引擎c#系统库的热补丁
目前只支持 Assets 下代码的热补丁,不支持引擎,C# 系统库的热补丁。
## API
xlua.hotfix(class, [method_name], fix)
`xlua.hotfix(class, [method_name], fix)`
* 描述 注入lua补丁
* class C#类两种表示方法CS.Namespace.TypeName或者字符串方式"Namespace.TypeName"字符串格式和C#的Type.GetType要求一致如果是内嵌类型Nested Type是非Public类型的话只能用字符串方式表示"Namespace.TypeName+NestedTypeName"
* method_name 方法名,可选;
* fix 如果传了method_namefix将会是一个function否则通过table提供一组函数。table的组织按key是method_namevalue是function的方式。
base(csobj)
`base(csobj)`
* 描述 子类override函数通过base调用父类实现。
* csobj 对象
@ -41,7 +46,7 @@ xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)
end)
```
util.hotfix_ex(class, method_name, fix)
`util.hotfix_ex(class, method_name, fix)`
* 描述 xlua.hotfix的增强版本可以在fix函数里头执行原来的函数缺点是fix的执行会略慢。
* method_name 方法名;
@ -53,9 +58,13 @@ util.hotfix_ex(class, method_name, fix)
方式一直接在类里头打Hotfix标签不建议示例只是为了方便演示采取这种方式
方式二在一个static类的static字段或者属性里头配置一个列表。属性可以用于实现的比较复杂的配置比如根据Namespace做白名单。
!!注意,方式一在高版本 Unity 不支持
~~~csharp
方式二:在一个静态类的静态字段或者属性里头配置一个列表。属性可以用于实现的比较复杂的配置,比如根据命名空间做白名单。
!!注意,高版本 Unity 需要把配置文件放 Editor 目录下
```csharp
//如果涉及到Assembly-CSharp.dll之外的其它dll如下代码需要放到Editor目录
public static class HotfixCfg
{
@ -77,7 +86,7 @@ public static class HotfixCfg
}
}
}
~~~
```
## Hotfix Flag
@ -85,37 +94,37 @@ Hotfix标签可以设置一些标志位对生成代码及插桩定制化
* Stateless、Stateful
遗留设置Stateful方式在新版本已经删除因为这种方式可以用xlua.util.state接口达到类似的效果该接口的使用可以看下HotfixTest2.cs里的示例代码。
遗留设置,`Stateful` 方式在新版本已经删除,因为这种方式可以用 `xlua.util.state` 接口达到类似的效果,该接口的使用可以看下`HotfixTest2.cs` 里的示例代码。
由于没Stateful默认就是Stateless所以也没必要设置该标志位。
由于没 `Stateful`,默认就是 `Stateless`,所以也没必要设置该标志位。
* ValueTypeBoxing
值类型的适配delegate会收敛到object好处是代码量更少不好的是值类型会产生boxing及gc适用于对text段敏感的业务。
值类型的适配delegate会收敛到object好处是代码量更少不好的是值类型会产生boxing及gc适用于对text段敏感的业务。
* IgnoreProperty
不对属性注入及生成适配代码,一般而言,大多数属性的实现都很简单,出错几率比较小,建议不注入。
不对属性注入及生成适配代码,一般而言,大多数属性的实现都很简单,出错几率比较小,建议不注入。
* IgnoreNotPublic
不对非public的方法注入及生成适配代码。除了像MonoBehaviour那种会被反射调用的私有方法必须得注入其它仅被本类调用的非public方法可以不注入只不过修复时会工作量稍大所有引用到这个函数的public方法都要重写。
不对非public的方法注入及生成适配代码。除了像MonoBehaviour那种会被反射调用的私有方法必须得注入其它仅被本类调用的非public方法可以不注入只不过修复时会工作量稍大所有引用到这个函数的public方法都要重写。
* Inline
不生成适配delegate直接在函数体注入处理代码。
不生成适配 delegate直接在函数体注入处理代码。
* IntKey
不生成静态字段,而是把所有注入点放到一个数组集中管理。
不生成静态字段,而是把所有注入点放到一个数组集中管理。
好处对text段影响小。
好处:对 text 段影响小。
坏处使用不像默认方式那么方便需要通过id来指明hotfix哪个函数而这个id是代码注入工具时分配的函数到id的映射会保存在Gen/Resources/hotfix_id_map.lua.txt并且自动加时间戳备份到hotfix_id_map.lua.txt同级目录发布手机版本后请妥善保存该文件。
坏处使用不像默认方式那么方便需要通过id来指明hotfix哪个函数而这个id是代码注入工具时分配的函数到id的映射会保存在 `Gen/Resources/hotfix_id_map.lua.txt`,并且自动加时间戳备份到 `hotfix_id_map.lua.txt` 同级目录,发布手机版本后请妥善保存该文件。
该文件的格式大概如下注意该文件仅IntKey模式使用当你没类型指定IntKey模式注入该文件只返回个空表
该文件的格式大概如下注意该文件仅IntKey模式使用当你没类型指定IntKey模式注入该文件只返回个空表
~~~lua
```lua
return {
["HotfixTest"] = {
[".ctor"] = {
@ -138,19 +147,19 @@ return {
},
},
}
~~~
```
想要替换HotfixTestUpdate函数你得
想要替换 `HotfixTest``Update` 函数,你得
~~~lua
```lua
CS.XLua.HotfixDelegateBridge.Set(7, func)
~~~
```
如果是重载函数将会一个函数名对应多个id比如上面的Add函数。
如果是重载函数,将会一个函数名对应多个 id比如上面的 `Add` 函数。
能不能自动化一些呢可以xlua.util提供了auto_id_map函数执行一次后你就可以像以前那样直接用类方法名去指明修补的函数。
能不能自动化一些呢?可以,`xlua.util` 提供了 `auto_id_map` 函数,执行一次后你就可以像以前那样直接用类,方法名去指明修补的函数。
~~~lua
```lua
(require 'xlua.util').auto_id_map()
xlua.hotfix(CS.HotfixTest, 'Update', function(self)
self.tick = self.tick + 1
@ -158,25 +167,24 @@ xlua.hotfix(CS.HotfixTest, 'Update', function(self)
print('<<<<<<<<Update in lua, tick = ' .. self.tick)
end
end)
~~~
前提是hotfix_id_map.lua.txt放到可以通过require 'hotfix_id_map'引用到的地方
```
前提是 `hotfix_id_map.lua.txt` 放到可以通过 `require 'hotfix_id_map'` 引用到的地方。
## 使用建议
* 对所有较大可能变动的类型加上Hotfix标识
* 建议用反射找出所有函数参数字段属性事件涉及的delegate类型标注CSharpCallLua
* 业务代码引擎API系统API需要在Lua补丁里头高性能访问的类型加上LuaCallCSharp
* 引擎API系统API可能被代码剪裁调C#无引用的地方都会被剪裁如果觉得可能会新增C#代码之外的API调用这些API所在的类型要么加LuaCallCSharp要么加ReflectionUse
* 对所有较大可能变动的类型加上 `Hotfix` 标识;
* 建议用反射找出所有函数参数、字段、属性、事件涉及的 delegate 类型,标注 `CSharpCallLua`
* 业务代码引擎 API系统 API需要在 Lua 补丁里头高性能访问的类型,加上 `LuaCallCSharp`
* 引擎 API系统 API 可能被代码剪裁调C#无引用的地方都会被剪裁),如果觉得可能会新增 C# 代码之外的 API 调用这些 API 所在的类型要么加 `LuaCallCSharp`,要么加 `ReflectionUse`
## 打补丁
xlua可以用lua函数替换C#的构造函数函数属性事件的替换lua实现都是函数比如属性对于一个getter函数和一个setter函数事件对应一个add函数和一个remove函数
xlua可以用lua函数替换 C# 的构造函数,函数,属性,事件的替换。lua实现都是函数比如属性对于一个 getter 函数和一个 setter 函数事件对应一个 `add` 函数和一个 `remove` 函数。
* 函数
method_name传函数名支持重载不同重载都是转发到同一个lua函数
`method_name` 传函数名,支持重载,不同重载都是转发到同一个 lua 函数
比如:
@ -207,41 +215,41 @@ end)
```
静态函数和成员函数的区别是成员函数会加一个self参数这个selfStateless方式下是C#对象本身对应C#的this
静态函数和成员函数的区别是成员函数会加一个 self 参数这个 selfStateless 方式下是 C# 对象本身(对应 C# 的 this
普通参数对于lua的参数ref参数对应lua的一个参数和一个返回值out参数对于lua的一个返回值
普通参数对于 lua 的参数ref 参数对应 lua 的一个参数和一个返回值out参数对于lua的一个返回值
泛化函数的打补丁规则和普通函数一样。
* 构造函数
构造函数对应的method_name".ctor"。
构造函数对应的 `method_name` 是 ".ctor"。
和普通函数不一样的是构造函数的热补丁并不是替换而是执行原有逻辑后调用lua。
* 属性
对于名为AProp的属性会对应一个gettermethod_name等于get_APropsettermethod_name等于set_AProp
对于名为 “AProp” 的属性,会对应一个 getter`method_name` 等于 `get_AProp`setter`method_name` 等于 `set_AProp`
* []操作符
赋值对应set_Item取值对应get_Item第一个参数是self赋值后面跟keyvalue取值只有key参数返回值是取出的值
赋值对应 `set_Item`取值对应 `get_Item`第一个参数是 self赋值后面跟 keyvalue取值只有 key 参数返回值是取出的值
* 其它操作符
C#的操作符都有一套内部表示比如+号的操作符函数名是op_Addition其它操作符的内部表示可以去请参照相关资料覆盖这函数就覆盖了C#的+号操作符
C#的操作符都有一套内部表示,比如 `+` 号的操作符函数名是 `op_Addition`其它操作符的内部表示可以去请参照相关资料覆盖这函数就覆盖了 C# 的 `+` 号操作符。
* 事件
比如对于事件AEvent”,+=操作符是add_AEvent-=对应的是remove_AEvent。这两个函数均是第一个参数是self第二个参数是操作符后面跟的delegate。
比如对于事件AEvent”+= 操作符是 add_AEvent-=对应的是 remove_AEvent。这两个函数均是第一个参数是self第二个参数是操作符后面跟的delegate。
通过xlua.private_accessible版本号大于2.1.11不需要调用xlua.private_accessible来直接访问事件对应的私有delegate的直接访问后可以通过对象的"&事件名"字段直接触发事件例如self\['&MyEvent'\]()其中MyEvent是事件名
通过 `xlua.private_accessible`版本号大于2.1.11不需要调用 `xlua.private_accessible`来直接访问事件对应的私有 delegate 的直接访问后可以通过对象的"&事件名"字段直接触发事件,例如 `self['&MyEvent']()`其中MyEvent是事件名
* 析构函数
method_name"Finalize"传一个self参数
method_name 是 "Finalize"传一个 self 参数
和普通函数不一样的是析构函数的热补丁并不是替换而是开头调用lua函数后继续原有逻辑
和普通函数不一样的是析构函数的热补丁并不是替换而是开头调用 lua 函数后继续原有逻辑
* 泛化类型
@ -253,10 +261,9 @@ public class GenericClass<T>
```
你只能对GenericClass\<double\>GenericClass\<int\>这些类而不是对GenericClass打补丁
你只能对 `GenericClass<double>``GenericClass<int>` 这些类,而不是对 `GenericClass` 打补丁。
对GenericClass<double>打补丁的实例如下:
`GenericClass<double>` 打补丁的实例如下:
```csharp
luaenv.DoString(@"
@ -277,9 +284,9 @@ luaenv.DoString(@"
* Unity协程
通过util.cs_generator可以用一个function模拟一个IEnumerator在里头用coroutine.yield就类似C#里头的yield return。比如下面的C#代码和对应的hotfix代码是等同效果的
通过 `util.cs_generator` 可以用一个 function 模拟一个 `IEnumerator`,在里头用 `coroutine.yield`,就类似 C# 里头的 yield return。比如下面的 C# 代码和对应的 hotfix 代码是等同效果的
~~~csharp
```csharp
[XLua.Hotfix]
public class HotFixSubClass : MonoBehaviour {
IEnumerator Start()
@ -291,9 +298,9 @@ public class HotFixSubClass : MonoBehaviour {
}
}
}
~~~
```
~~~csharp
```csharp
luaenv.DoString(@"
local util = require 'xlua.util'
xlua.hotfix(CS.HotFixSubClass,{
@ -307,11 +314,11 @@ luaenv.DoString(@"
end;
})
");
~~~
```
* 整个类
如果要替换整个类不需要一次次的调用xlua.hotfix去替换可以整个一次完成。只要给一个table按method_name = function组织即可
如果要替换整个类,不需要一次次的调用 `xlua.hotfix` 去替换,可以整个一次完成。只要给一个 table `method_name = function` 组织即可
```lua
@ -365,4 +372,3 @@ xlua.hotfix(CS.StatefullTest, {
})
```