Xlua热更新

前置:

lua

    Lua是一种轻量的小巧的脚本语言,使用标准C编写,支持面向过程编程和函数式编程。其代码运行流程:lua文件 --> Luac.exe编译 --> *.lua.byte文件 --> Lua虚拟机执行。

热更新

    热更新意为不停机更新,也泛指不需要重新安装整包的更新。热更新分为资源热更新和代码热更新,unity为资源热更新提供了AssetBundle,代码热更新却没有现成的方案。目前市面上流行的热更新方法包括xlua/tolua/slua框架和ILRuntime。

编译方式

    Mono是.Net的一种实现,将c#源码编译成IL汇编语言,整合其他第三方IL一起放在mono虚拟机中执行。Android平台上可以使用JIT模式运行IL,也可以使用AOT模式转成NativeCode交给CPU执行,甚至还有Full AOT。所以Android平台热更比较方便。IOS平台为了安全性考虑,无法给新开辟的内存空间设置可执行属性,而且当前IOS新上架应用必须支持64位,所以只能使用IL2Cpp来编译,顾名思义,它会将IL代码转成c++代码,再编译成原生汇编代码,放在虚拟机执行。

Xlua的使用

添加宏

    添加HOTFIX_ENABLE宏可以在编辑器模式下利用菜单进行注入。

执行Xlua/Generate Code

    生成代码,给标记Hotfix特性的方法生成对应的匹配函数。

执行Xlua/Hotfix Inject In Editor

    注入,触发HotfixInject方法,完成代码注入。

打补丁

    通过xlua.hotfx或xlua.hotfix_ex将c#函数逻辑替换成Lua函数。

Lua操作C#对象

    c#和Lua互相调用,就需要一个通信机制进行数据传递。Lua是由C语言编写的,所以c#可以通过P/Invoke方式借助C/C++与Lua进行通信。C/C++和Lua的数据通信通过栈进行,将数据拷贝到栈中,通过索引值在另一边定位,每一种数据都有相应的存取接口。
    要传递一个C#对象给Lua,因为Lua没有能与之对应的类型,因此传递到Lua的只是C#对象的一个索引。具体可以参考Src/ObjectTranslator脚本的Push方法。
    此方法负责将对象的索引push到lua,但也对引用和枚举进行了缓存,并尝试从其中取值。注意这个缓存在c#侧和Lua侧都有,c#侧缓存的是对象,lua侧缓存的是c#缓存的对象的索引。Lua侧拿到索引后,会为其创建一个usedata指向索引。Lua就是利用这个userdata指向的对象索引和c#对象保持联系。
    userdata不可能只包含索引,实际上它包含着该对象的类型信息,有哪些成员方法、属性、静态方法等,这些信息都包含在userdata的元表中,这个元表也是在c#侧获取,通过Lua注册表和返回的索引进行设置,具体可以看Push方法中的getTypeId调用,元表的生成有两种方法,利用生成代码填充或利用反射填充。

生成代码

    生成函数需要打上[LuaCallCSharp]标签,执行Generate Code之后会生成一个XXXWrap的文件,其中除了__Register函数__CreateInstance函数之外,会为所有类的非静态字段生成对应的包裹方法,即get和set。通过一个例子来看一下生成函数_Register的作用
比如:

[LuaCallCSharp]
public class TestXlua
{
    public static LuaTable tab;

    public string str;
    public int luaIdx;
    public void Test() { }
    public void Test2() { }

    public static void StaticTest() { }
    public static void StaticTest2() { }
}

对应的_Register

public static void __Register(RealStatePtr L)
{
	ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
	System.Type type = typeof(TestXlua);
	//给type创建元表,并设置到注册表中,为元表添加_gc(lua通知c#可以回收对象)和—_tostring元方法,准备好对应数量非静态方法的method表和非静态变量的getter和setter表
	Utils.BeginObjectRegister(type, L, translator, 0, 2, 2, 2);
	
	//将非静态的方法和变量,即非静态值对应的包裹方法注册到不同的lua表中。
	//_m_Test,_g_get_str,_s_set_str等方法是Generate Coe时动态生成的
	//可以看到_m_Test等生成方法只是对原来方法生成一层包裹,其本质就是调用原来的方法
	//生成包裹方法可以更方便的使用Lua中的栈进行参数传递,调用原方法之前参数以正序入栈并返回参数个数,当需要返回值的时候,同样将返回值正序入栈,返回参数个数。(多返回值可以实现c#中ref和out参数特性)。这就解释了为什么要为属性设置set和get方法,因为只有函数调用,才能利用这种自动压栈的特性,传递给c#
	Utils.RegisterFunc(L, Utils.METHOD_IDX, "Test", _m_Test);
	Utils.RegisterFunc(L, Utils.METHOD_IDX, "Test2", _m_Test2);
	Utils.RegisterFunc(L, Utils.GETTER_IDX, "str", _g_get_str);
    Utils.RegisterFunc(L, Utils.GETTER_IDX, "luaIdx", _g_get_luaIdx);
    Utils.RegisterFunc(L, Utils.SETTER_IDX, "str", _s_set_str);
    Utils.RegisterFunc(L, Utils.SETTER_IDX, "luaIdx", _s_set_luaIdx);
    
	//将注册获得的metods表、getters表压入栈,并为元表生成__index元方法和__newindex原方法。也是Lua调用C#的核心
	Utils.EndObjectRegister(type, L, translator, null, null,
	    null, null, null);

    //对类的静态方法和静态变量做注册前的准备。与非静态注册过程不同的是,此处会为类生成cls_table表以及它的元表。
    //cls_table是根据命名空间来逐层添加到注册表的,比如A.B.C,通过SetCSTable后的结构是注册表[xlua_csharp_namespace][A][B][C]={}
    //xlua_csharp_namespace是CS全局表,在Lua注册表中,所以xlua中访问C#类时可以直接使用CS.A.B.C这种方式
    Utils.BeginClassRegister(type, L, __CreateInstance, 3, 1, 1);
    
    //注册包裹方法到Lua表中
	Utils.RegisterFunc(L, Utils.CLS_IDX, "StaticTest", _m_StaticTest_xlua_st_);
    Utils.RegisterFunc(L, Utils.CLS_IDX, "StaticTest2", _m_StaticTest2_xlua_st_);
    Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "tab", _g_get_tab);
	Utils.RegisterFunc(L, Utils.CLS_SETTER_IDX, "tab", _s_set_tab);
    
	//为cls_table的元表meta_table设置__index和__newindex元方法
	Utils.EndClassRegister(type, L, translator);
}

    总结就是,生成代码为类的非静态值都生成了包裹方法,register将包裹方法注册到不同的表里,为userdata注册的__index和__newindex方法负责从不同的表中找到对应的包裹方法,最终实现对c#对象方法的调用。除此之外,还为每个类生成了cls_table表,将类的静态方法的包裹方法,以及静态变量的getter和setter方法直接注册到cs_table表,其元表meta_table的__index和__newindex元方法负责找到对应的包裹方法,以实现调用。

反射

    如果没有生成代码,会通过反射进行注册。其与生成代码进行注册的逻辑基本相同。获取到类的静态和非静态值,注册到不同的表中,设置元表,填充__index和__newindex元方法等。可以查看Utils.ReflectionWrap方法

下一篇:C#如何接收操作Lua对象

参考:

深入浅出Lua虚拟机
深入xlua实现原理之Lua如何调用C#

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值