xlua热修复的使用方法:

功能开启步骤:
(1)特性宏定义:HOTFIX_ENABLE;
    平时不开,build版本时动态打开。
(2)执行:XLua/Generate Code,生成各种辅助代码;
(3)执行:XLua/Hotfix Inject In Editor,对DLL进行注入。
    手机版本build时自动进行;
    Editor下手动调用;
    打印hotfix inject finish!才算成功。
修复函数:
    xlua.hotfix(class, [method_name], fix)
    util.hotfix_ex(class, method_name, fix)
    增强版,速度略慢

待修复C#代码:

public class CSharpClass
{
    public int iValue = 100;
    public string strValue = "";

    public CSharpClass()
    {
        iValue = 1;
        strValue = "default";
    }

    public void Print()
    {
        UnityEngine.Debug.LogError($"call print: {iValue}, {strValue}");
    }

    public int Add(int v)
    {
        iValue += v;
        return iValue;
    }
}

指定哪些类型需要进行热修复,放在Editor目录下:

public static class HotfixCfg
{
    [XLua.Hotfix]
    public static List<Type> by_field = new List<Type>()
    {
        typeof(CSharpClass),
    };
}

lua代码,其中使用了xlua.hotfix函数进行C#函数修改:

local function Print_hotfix(self)
    print("Print_hotfix")
end

-- 热修复
xlua.hotfix(CS.CSharpClass, 'Print', Print_hotfix)

local obj = CS.CSharpClass()
obj.strValue = "strFromLua"
obj:Print()

-- 需要手动置空,不然会有引用泄露
xlua.hotfix(CS.CSharpClass, 'Print', nil)

热修复原理分析:

可以看到需要热修复的函数的IL代码添加了一些插桩代码,如果hotfix0_xxx不为空则走hotfix,否则保持原逻辑。
    首先看一下xlua.hotfix这个lua函数的实现:
    LuaEnv.cs line 553 定义了xlua.hotfix函数:该函数所做的事情就是cs.__Hotfix0_funcname = func

xlua.hotfix = function(cs, field, func)
    if func == nil then func = false end
    local tbl = (type(field) == 'table') and field or {[field] = func}
    for k, v in pairs(tbl) do
        local cflag = ''
        if k == '.ctor' then
            cflag = '_c'
            k = 'ctor'
        end
        local f = type(v) == 'function' and v or nil
        xlua.access(cs, cflag .. '__Hotfix0_'..k, f) -- at least one
        pcall(function()
            for i = 1, 99 do
                xlua.access(cs, cflag .. '__Hotfix'..i..'_'..k, f)
            end
        end)
    end
    xlua.private_accessible(cs)
end

    ObjectTranslator.cs的line 690 定义了xlua.access函数的实现:

LuaAPI.xlua_pushasciistring(L, "access");
LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.XLuaAccess);
LuaAPI.lua_rawset(L, -3);
LuaAPI.xlua_pushasciistring(L, "private_accessible");
LuaAPI.lua_pushstdcallcfunction(L, StaticLuaCallbacks.XLuaPrivateAccessible);
LuaAPI.lua_rawset(L, -3);

    StaticLuaCallbacks.XLuaAccess的函数实现:

[MonoPInvokeCallback(typeof(LuaCSFunction))]
public static int XLuaAccess(RealStatePtr L)
{
    try
    {
        ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
        Type type = getType(L, translator, 1);
        object obj = null;
        // ...string fieldName = LuaAPI.lua_tostring(L, 2);

        BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static;

        if (LuaAPI.lua_gettop(L) > 2) // set
        {
            var field = type.GetField(fieldName, bindingFlags);
            if (field != null)
            {
                field.SetValue(obj, translator.GetObject(L, 3, field.FieldType));
                return 0;
            }
            var prop = type.GetProperty(fieldName, bindingFlags);
            if (prop != null)
            {
                prop.SetValue(obj, translator.GetObject(L, 3, prop.PropertyType), null);
                return 0;
            }
        }
        // ...
}

    实现中通过反射来设置csharp class的静态变量的值。(lua的基本操作都是基于堆栈进行的,上述代码看不懂的可以在帖子下面留言。)
从生成的IL代码可以看到,构造函数和普通成员函数的修复行为也不一样。
    普通成员函数:使用lua函数覆盖;
    构造函数:先执行C#原有的构造函数,再执行lua的函数。

posted @ 2022-02-27 22:30 斯芬克斯 阅读(88) 评论(0) 推荐(0) 编辑
摘要: 阅读全文
posted @ 2021-06-24 16:16 斯芬克斯 阅读(77) 评论(0) 推荐(0) 编辑
摘要: 阅读全文
posted @ 2021-05-11 20:37 斯芬克斯 阅读(91) 评论(0) 推荐(0) 编辑
摘要: 当我们想要在多个Component之间共享数据时,会想到哪些方案呢? (1)SritptableObject (2)NativeArray (3)ISharedDataComponent (4)BlobAsset (5)unsafe void* (6)... 不错,这些都是常用手段,但是区别甚大,并 阅读全文
posted @ 2020-06-10 23:54 斯芬克斯 阅读(404) 评论(0) 推荐(0) 编辑
摘要: 各个工作线程的随机数种子是稳定的,RandomSystem的实现如下: using Unity.Entities; using Unity.Jobs; using Unity.Burst; using Unity.Collections; using Unity.Jobs.LowLevel.Unsa 阅读全文
posted @ 2020-06-10 21:37 斯芬克斯 阅读(904) 评论(2) 推荐(0) 编辑
摘要: 将一个方块的AnimationClip数据导出,然后以BlobAsset的方式在JobSystem中并行计算,以提升效率: (1)编辑态,将AnimationClip转为SriptableObject数据,进行存储; (2)运行时,将ScriptableObject数据转换为BlobAsset对象, 阅读全文
posted @ 2020-04-01 03:04 斯芬克斯 阅读(2464) 评论(0) 推荐(0) 编辑
摘要: 按照一般的写法,write会产生race condition,所以需要通过依赖关系按顺序执行: using UnityEngine; using Unity.Jobs; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; 阅读全文
posted @ 2020-03-08 23:51 斯芬克斯 阅读(3251) 评论(0) 推荐(0) 编辑
摘要: 定义: 使用WriteGroup标记了同一个component的components就属于一个WriteGroup: public struct W : IComponentData { public int Value; } [WriteGroup(typeof(W))] public struc 阅读全文
posted @ 2020-02-29 21:58 斯芬克斯 阅读(746) 评论(0) 推荐(0) 编辑
摘要: 结构性修改Structural changes: 任何导致entity的原型(archetype)变化,或者entity在chunk中的存储位置变化的修改,都叫做结构性修改。 以下操作皆为结构性修改: 创建或销毁entity 添加或移除component 修改shared component的值 同 阅读全文
posted @ 2020-02-29 21:57 斯芬克斯 阅读(303) 评论(0) 推荐(0) 编辑
摘要: ECS systems的主要任务就是读取一系列的components数据,计算以后将结果写入其他的components。那么如何高效地读写数据就是很重要的,而有效的方法就是利用cpu的多核特性来并行地读取和处理数据。 ECS提供了多种完成这个任务的方式,每一种都有自己的规则和约束: API 说明 J 阅读全文
posted @ 2020-02-29 16:16 斯芬克斯 阅读(1847) 评论(0) 推荐(1) 编辑
点击右上角即可分享
微信分享提示