Unity XLua Hotfix热更新配置笔记

article/2025/9/28 2:40:58

Unity XLUA Hotfix热更新配置笔记


目录

Unity XLUA Hotfix热更新配置笔记


配置热更新步骤:

  • 下载XLUA
  • 下载压缩包解压 复制xlua 和plugins到assets
  • 开启热补丁特性 先添加宏 HOTFIX_ENABLE;INJECT_WITHOUT_TOOL
  • 报“This delegate/interface must add to CSharpCallLua : XXX”异常怎么解决?
  • .require XXX 调用Lua脚本 具体脚本参考官方文档 lua文件放在Resources下
  • Tips:

 

  • 下载XLua

  • 传送门

官网:

https://github.com/Tencent/xLua

配置文档:

https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md

官方FAQ:

https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/faq.md

代码生成引擎:

https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/custom_generate.md

Lua5.3官方手册:

https://www.lua.org/manual/5.3/manual.html

 

  • 下载压缩包解压 复制XLua和plugins到assets,Tool复制在Assets同级目录下

  • 开启热补丁特性 先添加宏 HOTFIX_ENABLE;INJECT_WITHOUT_TOOL

注意:两个宏都要添加(),官方FAQ说

报“This delegate/interface must add to CSharpCallLua : XXX”异常怎么解决?

在编辑器下xLua不生成代码都可以运行,出现这种提示,要么是该类型没加CSharpCallLua,要么是加之前生成过代码,没重新执行生成。

解决办法,确认XXX(类型名)加上CSharpCallLua后,清除代码后运行。

如果编辑器下没问题,发布到手机报这错,表示你发布前没生成代码(执行“XLua/Generate Code”)。

亲测两个宏都必须要加

 

HOTFIX_ENABLE:开启热更新特性,编辑器,各手机平台需要手动单独设置,如果是自动化打包,要注意在代码里用API设置的宏是不生效的,需要在编辑器里设置。
     INJECT_WITHOUT_TOOL:采用内嵌到编辑器的方式注入。添加了该宏之后,在菜单栏XLUA里才能够进行【Hotfix inject in Editor】的操作;在构建手机包的时候这个步骤会在构建时自动进行,编辑器模式下开发补丁需要手动执行Hotfix inject in Editor进行注入。
     定义INJECT_WITHOUT_TOOL宏后,热补丁特性依赖Cecil,添加HOTFIX_ENABLE宏之后,可能会报找不到Cecil。这时你需要到Unity安装目录下找到Mono.Cecil.dll,Mono.Cecil.Pdb.dll,Mono.Cecil.Mdb.dll,拷贝到项目里头。
    注意:如果你的Unity安装目录没有Mono.Cecil.Pdb.dll,Mono.Cecil.Mdb.dll(往往是一些老版本),那就只拷贝Mono.Cecil.dll(你从别的版本的Unity拷贝一套可能会导致编辑器不稳定),这时你需要定义HOTFIX_SYMBOLS_DISABLE,
    这会导致C#代码没法调试以及Log的栈源文件及行号错乱(所以赶紧升级Unity)。

    以上两个宏若是不开启,或者在没有成功进行Hotfix inject in Editor操作直接运行的话,系统会抛出 LuaException: xlua.access, no field __Hotfix0_hotfix

 

随后执行XLua/Generate Code菜单。热补丁需要执行XLua/Generate Code才能正常运行。

注入,构建手机包这个步骤会在构建时自动进行,编辑器下开发补丁需要手动执行”XLua/Hotfix Inject In Editor“菜单。

注入成功会打印“hotfix inject finish!”或者“had injected!”。

 

  • .require XXX 调用Lua脚本 具体脚本参考官方文档 lua文件放在Resources下

main.lua.txt

local list = require("PatchList");
for _,filename in ipairs(list) dorequire(filename);
end

PatchList.lua.txt

return {"Patch1",
};

Patch1.lua.txt

---xlua.private_accessible(CS.MyMessage.ShowMessage);  这个似乎有bugxlua.hotfix(CS.MyMessage.ShowMessage, 'show', function(self)  local go= CS.UnityEngine.GameObject.Find("HotfixTextTip")go.GetComponent(go,"Text").text="热更新完成"
end)

ShowMessage.cs

namespace MyMessage
{public class ShowMessage{public void Update(){show();}void show(){GameObject go = GameObject.Find("message");go.GetComponent<Text>().text = "公告:暂无公告";}}
}

 

  • Tips:

反复执行lua文件需要重新构建LuaEnv

热更新类和函数需要标签[Hotfix]或者
 [Hotfix]
        public static List<Type> hotfixList
        {
            get
            {
                string[] allowNamespaces = new string[] {
                    "命名空间",
                };

                return (from type in Assembly.Load("Assembly-CSharp").GetTypes()
                        where allowNamespaces.Contains(type.Namespace)
                        select type).ToList();
            }

        }
在Assets\XLua\Src\Editor 使用 HotfixConfig.cs 添加白名单


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_name,fix将会是一个function,否则通过table提供一组函数。table的组织按key是method_name,value是function的方式。


  • 官方文档

https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md

 


官方文档

  • 使用方式
  • 约束
  • API
  • 标识要热更新的类型
  • Hotfix Flag
  • 使用建议
  • 打补丁

使用方式

1、添加HOTFIX_ENABLE宏打开该特性(在Unity3D的File->Build Setting->Scripting Define Symbols下添加)。编辑器、各手机平台这个宏要分别设置!如果是自动化打包,要注意在代码里头用API设置的宏是不生效的,需要在编辑器设置。

(建议平时开发业务代码不打开HOTFIX_ENABLE,只在build手机版本或者要在编译器下开发补丁时打开HOTFIX_ENABLE)

2、执行XLua/Generate Code菜单。

3、注入,构建手机包这个步骤会在构建时自动进行,编辑器下开发补丁需要手动执行"XLua/Hotfix Inject In Editor"菜单。注入成功会打印“hotfix inject finish!”或者“had injected!”。

约束

不支持静态构造函数。

目前只支持Assets下代码的热补丁,不支持引擎,c#系统库的热补丁。

API

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_name,fix将会是一个function,否则通过table提供一组函数。table的组织按key是method_name,value是function的方式。

base(csobj)

  • 描述 : 子类override函数通过base调用父类实现。
  • csobj : 对象
  • 返回值 : 新对象,可以通过该对象base上的方法

例子(位于HotfixTest2.cs):

xlua.hotfix(CS.BaseTest, 'Foo', function(self, p)print('BaseTest', p)base(self):Foo(p)
end)

util.hotfix_ex(class, method_name, fix)

  • 描述 : xlua.hotfix的增强版本,可以在fix函数里头执行原来的函数,缺点是fix的执行会略慢。
  • method_name : 方法名;
  • fix : 用来替换C#方法的lua function。

标识要热更新的类型

和其它配置一样,有两种方式

方式一:直接在类里头打Hotfix标签(不建议,示例只是为了方便演示采取这种方式);

方式二:在一个static类的static字段或者属性里头配置一个列表。属性可以用于实现的比较复杂的配置,比如根据Namespace做白名单。

//如果涉及到Assembly-CSharp.dll之外的其它dll,如下代码需要放到Editor目录
public static class HotfixCfg
{[Hotfix]public static List<Type> by_field = new List<Type>(){typeof(HotFixSubClass),typeof(GenericClass<>),};[Hotfix]public static List<Type> by_property{get{return (from type in Assembly.Load("Assembly-CSharp").GetTypes()where type.Namespace == "XXXX"select type).ToList();}}
}

Hotfix Flag

Hotfix标签可以设置一些标志位对生成代码及插桩定制化

  • Stateless、Stateful

遗留设置,Stateful方式在新版本已经删除,因为这种方式可以用xlua.util.state接口达到类似的效果,该接口的使用可以看下HotfixTest2.cs里的示例代码。

由于没Stateful,默认就是Stateless,所以也没必要设置该标志位。

  • ValueTypeBoxing

值类型的适配delegate会收敛到object,好处是代码量更少,不好的是值类型会产生boxing及gc,适用于对text段敏感的业务。

  • IgnoreProperty

不对属性注入及生成适配代码,一般而言,大多数属性的实现都很简单,出错几率比较小,建议不注入。

  • IgnoreNotPublic

不对非public的方法注入及生成适配代码。除了像MonoBehaviour那种会被反射调用的私有方法必须得注入,其它仅被本类调用的非public方法可以不注入,只不过修复时会工作量稍大,所有引用到这个函数的public方法都要重写。

  • Inline

不生成适配delegate,直接在函数体注入处理代码。

  • IntKey

不生成静态字段,而是把所有注入点放到一个数组集中管理。

好处:对text段影响小。

坏处:使用不像默认方式那么方便,需要通过id来指明hotfix哪个函数,而这个id是代码注入工具时分配的,函数到id的映射会保存在Gen/Resources/hotfix_id_map.lua.txt,并且自动加时间戳备份到hotfix_id_map.lua.txt同级目录,发布手机版本后请妥善保存该文件。

该文件的格式大概如下(注意:该文件仅IntKey模式使用,当你没类型指定IntKey模式注入,该文件只返回个空表):

return {["HotfixTest"] = {[".ctor"] = {5},["Start"] = {6},["Update"] = {7},["FixedUpdate"] = {8},["Add"] = {9,10},["OnGUI"] = {11},},
}

想要替换HotfixTest的Update函数,你得

CS.XLua.HotfixDelegateBridge.Set(7, func)

如果是重载函数,将会一个函数名对应多个id,比如上面的Add函数。

能不能自动化一些呢?可以,xlua.util提供了auto_id_map函数,执行一次后你就可以像以前那样直接用类,方法名去指明修补的函数。

(require 'xlua.util').auto_id_map()
xlua.hotfix(CS.HotfixTest, 'Update', function(self)self.tick = self.tick + 1if (self.tick % 50) == 0 thenprint('<<<<<<<<Update in lua, tick = ' .. self.tick)endend)

前提是hotfix_id_map.lua.txt放到可以通过require 'hotfix_id_map'引用到的地方。

使用建议

  • 对所有较大可能变动的类型加上Hotfix标识;
  • 建议用反射找出所有函数参数、字段、属性、事件涉及的delegate类型,标注CSharpCallLua;
  • 业务代码、引擎API、系统API,需要在Lua补丁里头高性能访问的类型,加上LuaCallCSharp;
  • 引擎API、系统API可能被代码剪裁调(C#无引用的地方都会被剪裁),如果觉得可能会新增C#代码之外的API调用,这些API所在的类型要么加LuaCallCSharp,要么加ReflectionUse;

打补丁

xlua可以用lua函数替换C#的构造函数,函数,属性,事件的替换。lua实现都是函数,比如属性对于一个getter函数和一个setter函数,事件对应一个add函数和一个remove函数。

  • 函数

method_name传函数名,支持重载,不同重载都是转发到同一个lua函数。

比如:

// 要fix的C#类
[Hotfix]
public class HotfixCalc
{public int Add(int a, int b){return a - b;}public Vector3 Add(Vector3 a, Vector3 b){return a - b;}
}
xlua.hotfix(CS.HotfixCalc, 'Add', function(self, a, b)return a + b
end)

静态函数和成员函数的区别是,成员函数会加一个self参数,这个self在Stateless方式下是C#对象本身(对应C#的this)

普通参数对于lua的参数,ref参数对应lua的一个参数和一个返回值,out参数对于lua的一个返回值。

泛化函数的打补丁规则和普通函数一样。

  • 构造函数

构造函数对应的method_name是".ctor"。

和普通函数不一样的是,构造函数的热补丁并不是替换,而是执行原有逻辑后调用lua。

  • 属性

对于名为“AProp”的属性,会对应一个getter,method_name等于get_AProp,setter的method_name等于set_AProp。

  • []操作符

赋值对应set_Item,取值对应get_Item。第一个参数是self,赋值后面跟key,value,取值只有key参数,返回值是取出的值。

  • 其它操作符

C#的操作符都有一套内部表示,比如+号的操作符函数名是op_Addition(其它操作符的内部表示可以去请参照相关资料),覆盖这函数就覆盖了C#的+号操作符。

  • 事件

比如对于事件“AEvent”,+=操作符是add_AEvent,-=对应的是remove_AEvent。这两个函数均是第一个参数是self,第二个参数是操作符后面跟的delegate。

通过xlua.private_accessible(版本号大于2.1.11不需要调用xlua.private_accessible)来直接访问事件对应的私有delegate的直接访问后,可以通过对象的"&事件名"字段直接触发事件,例如self['&MyEvent'](),其中MyEvent是事件名。

  • 析构函数

method_name是"Finalize",传一个self参数。

和普通函数不一样的是,析构函数的热补丁并不是替换,而是开头调用lua函数后继续原有逻辑。

  • 泛化类型

其它规则一致,需要说明的是,每个泛化类型实例化后都是一个独立的类型,只能针对实例化后的类型分别打补丁。比如:

public class GenericClass<T>
{
}

你只能对GenericClass<double>,GenericClass<int>这些类,而不是对GenericClass打补丁。

对GenericClass打补丁的实例如下:

luaenv.DoString(@"xlua.hotfix(CS.GenericClass(CS.System.Double), {['.ctor'] = function(obj, a)print('GenericClass<double>', obj, a)end;Func1 = function(obj)print('GenericClass<double>.Func1', obj)end;Func2 = function(obj)print('GenericClass<double>.Func2', obj)return 1314end})
");
  • Unity协程

通过util.cs_generator可以用一个function模拟一个IEnumerator,在里头用coroutine.yield,就类似C#里头的yield return。比如下面的C#代码和对应的hotfix代码是等同效果的

[XLua.Hotfix]
public class HotFixSubClass : MonoBehaviour {IEnumerator Start(){while (true){yield return new WaitForSeconds(3);Debug.Log("Wait for 3 seconds");}}
}
luaenv.DoString(@"local util = require 'xlua.util'xlua.hotfix(CS.HotFixSubClass,{Start = function(self)return util.cs_generator(function()while true docoroutine.yield(CS.UnityEngine.WaitForSeconds(3))print('Wait for 3 seconds')endend)end;})
");
  • 整个类

如果要替换整个类,不需要一次次的调用xlua.hotfix去替换,可以整个一次完成。只要给一个table,按method_name = function组织即可

xlua.hotfix(CS.StatefullTest, {['.ctor'] = function(csobj)return util.state(csobj, {evt = {}, start = 0, prop = 0})end;set_AProp = function(self, v)print('set_AProp', v)self.prop = vend;get_AProp = function(self)return self.propend;get_Item = function(self, k)print('get_Item', k)return 1024end;set_Item = function(self, k, v)print('set_Item', k, v)end;add_AEvent = function(self, cb)print('add_AEvent', cb)table.insert(self.evt, cb)end;remove_AEvent = function(self, cb)print('remove_AEvent', cb)for i, v in ipairs(self.evt) doif v == cb thentable.remove(self.evt, i)breakendendend;Start = function(self)print('Start')for _, cb in ipairs(self.evt) docb(self.start, 2)endself.start = self.start + 1end;StaticFunc = function(a, b, c)print(a, b, c)end;GenericTest = function(self, a)print(self, a)end;Finalize = function(self)print('Finalize', self)end
})

 


http://chatgpt.dhexx.cn/article/18I6XXIy.shtml

相关文章

xLua热更新(一)xLua基本使用

一、什么是xLua xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力&#xff0c;借助xLua&#xff0c;这些Lua代码可以方便的和C#相互调用。 xLua是用来实现Lua代码与C#代码相互调用的插件。我们可以借助这个插件来实现热更新方案。 那么为什么要选择Lua实现热更新呢&am…

Bug-CTF-秋名山老司机(正则匹配)

题目: 没有啥思路&#xff0c;意外地刷新了一下页面&#xff0c;发现数值变化了 再刷新一次试试&#xff0c;出来一个提示&#xff0c;大概意思是需要提交结果&#xff0c;这里也不知道该怎么传参&#xff0c;也不晓得怎么写这个脚本&#xff0c;只能参考其他大佬的思路了 解题…

BUGKU------秋名山老司机

看到这个就直接上python吧&#xff0c;用eval计算子式 import requests from bs4 import BeautifulSoup r requests.session() s r.get(http://123.206.87.240:8002/qiumingshan/) soup BeautifulSoup(s.text, "html.parser") a soup.find(div) d {"valu…

bugku秋名山车神

不断的刷新&#xff0c;发现表达式一直在变换&#xff0c;这种必须写脚本&#xff0c;才能跟上速度。直接上代码 import re import requests srequests.session() rs.get("http://123.206.87.240:8002/qiumingshan/") searchObj re.search(r^<div>(.*)\?;<…

【BugkuCTF】Web--秋名山老司机

Description: http://123.206.87.240:8002/qiumingshan/ 是不是老司机试试就知道。 Solution: 打开网页 2秒解决问题真是稳稳的写脚本……但是不知道提交啥&#xff0c;刷新网页看看提示让用POST方式传递一个value变量&#xff0c;构造脚本 import requests import re url htt…

CTF-web-秋名山老司机

前言&#xff1a;小编也是现学现卖&#xff0c;方便自己记忆&#xff0c;写的不好的地方还请包涵&#xff0c;也欢迎各位大佬多多批评指正。 网址&#xff1a;秋名山老司机 1.打开网址&#xff0c;提示需要两秒内计算出数值&#xff0c;手工几乎不可能实现。 2.思路:利用pyt…

秋名山车神

解题思路&#xff1a;看到这种题要在两秒类算出&#xff0c;人工肯定不可能&#xff0c;直接上脚本&#xff0c;由于我不会写python 脚本&#xff0c;直接在网上找了一篇大佬的脚本 import requests #安装requests库 import re url http://114.67.246.176:16847 #改为自己题…

爬虫笔记-Bugku秋名山老司机(入门)

记一次python爬虫笔记 题目&#xff1a;bugku-秋名山老司机 题目要求&#xff1a;两秒内提交一道很长的计算题答案&#xff0c;并且式子每次刷新都会变动 如&#xff1a; 多刷新几次可见题目提示&#xff0c;需要用post传入值&#xff0c;变量名为value 创建py文件&#xf…

ctf靶场-bugku-秋名山老司机,速度要快

页面快速计算(秋名山老司机) 1.靶场网址 http://123.206.87.240:8002/qiumingshan/ 2.脚本实现 import requests import re url"http://123.206.87.240:8002/qiumingshan/" srequests.Session() #储存session rs.get(url) #用此身份执行get请求 searchobjre.sea…

[bugku]-秋名山车神详解

解题 每一次刷新都不一样 post传参value 脚本1 import requests import re url http://114.67.175.224:10053/ s requests.Session() source s.get(url) expression re.search(r(\d[\-*])(\d), source.text).group() result eval(expression) post {value: result} prin…

Bugku_Web18_秋名山车神

1.查看源码 <head> <title>下面的表达式的值是秋名山的车速</title> <meta charset"UTF-8"> </head> <p>亲请在2s内计算老司机的车速是多少</p> <div>373719747-1878154638-1233431774-1476346255*1056350133121800…

bugku 秋名山车神

get新知识&#xff1a;一些有关python爬虫的基本知识 解题部分 题目中都是这样的大数字进行计算&#xff0c;并且需要短时间内计算&#xff0c;所以这不得不使用脚本进行解题&#xff0c;脚本如下 #bugku 秋名山车神 --爬虫练习 import requests import re ​ srequests.Sess…

web——秋名山老司机(100)——Bugku

000 靶场链接 http://123.206.87.240:8002/web16/ 001 题目描述 002 解题过程 一看大数运算就觉得要用python&#xff0c;然后就只能找wp看看 多刷新几次会有提示出来 一个value post 创建步骤 先建立文本文档&#xff0c;后缀改为.py&#xff0c;然后右键使用IDE打开 代…

求秋名山老司机车速

http://120.24.86.145:8002/qiumingshan/ 两秒内算出秋名山车神的车速。感觉很吊的。 刚开始知道的大概的知道就是算出答案&#xff0c;但是怎么提交&#xff0c;后来多刷新几次发现。 给我post value。好吧&#xff0c;翻译也翻译的很蛋疼。反正就是需要value这个作为提交的参…

秋名山老司机从上车到翻车的悲痛经历,带你深刻了解什么是 Spark on Hive!| 原力计划...

作者 | Alice菌 责编 | 夕颜 出品 | CSDN博客 本篇博客将为大家分享的内容是如何实现Spark on Hive&#xff0c;即让Hive只作为存储角色&#xff0c;Spark负责sql解析优化&#xff0c;执行…话不多说&#xff0c;直接上车&#xff01; 上车前需知 Spark on hive 与 hive on spa…

秋名山老司机 (Bugku) re库和request库

尝试写的第一个python脚本……之前一直只会用工具&#xff08;不&#xff0c;有的工具也还不会用……&#xff09;可以说是很神奇了 先贴上代码&#xff1a; import requests import re urlhttp://120.24.86.145:8002/qiumingshan/ rrequests.session() requestpage r.get(u…

bagku秋名山老司机

看题目,要求两秒内计算数值,发回去,获得flag,于是写脚本实现 import requests import reurl "http://120.24.86.145:8002/qiumingshan/" s requests.Session()#必须利用会话对象 Session()&#xff0c;否则提交结果的时候&#xff0c;页面又重新生成一个新的表达式…

秋名山老司机(详解)——bugku

刚刚做了bugku的题目&#xff0c;现在整理一下 写出解题思路&#xff0c;希望能够帮助到那些需要帮助的人 所有的wp都是以一题一篇的形式写出 主要是为了能够让读者更好的阅读以及查找&#xff0c; 希望你们不要责怪&#xff01;&#xff01;共勉&#xff01;&#xff01;&…

Bugku之秋名山老司机

秋名山老司机需要在2s内计算出来并提交&#xff0c;这个通过人工是不可能的&#xff0c;所以只能通过自己写脚本来计算并立即提交。 脚本如下也带有注释&#xff1a; import re import requestss requests.Session() r s.get("http://120.24.86.145:8002/qiumingshan…

bugKuctf-秋名山老司机

http://123.206.87.240:8002/qiumingshan/ 刷新几次发现需要把值post进去。 于是编辑脚本&#xff1a; encodingutf8 import re import requests s requests.Session() url ‘http://123.206.87.240:8002/qiumingshan/’ source s.get(url)#获取页面对象 expression re…