找回密码
 立即注册
查看: 303|回复: 3

ToLua#暖更新

[复制链接]
发表于 2021-8-13 13:59 | 显示全部楼层 |阅读模式
ToLua#暖更新特性简单说明
ToLua#暖更新使用说明
    ToLua#1.0.8版本更新带来了暖更新特性,主要用于比较灵活的解决线上版的部分C#代码BUG。也跟其他的所有的热更新Hook方案一样,由于无法在iOS上开启jit,故也无法新增或者删除已有的C#函数接口、数据类型等。目前只能在Lua端替换或者监听非泛型C#函数的具体实现。支持mono、il2cpp,可执行文件大小会变大,可以查看最开头两个文档链接,过滤掉自己项目不需要的Inject方式。本方案主张“半全量”自动hook过滤列表之外的所有可以hook的函数,不必精细化的导出每一个可能需要在线Hook的函数,偏向于“模块化”的过滤。故提供了一个较方便的黑名单生成工具,方便以文件夹为单位(比如过滤掉NGUI、Spine等第三方插件库)去过滤。结合着黑名单生成工具,包体的可执行文件不会增长太多,都可以实践下,视各自的项目而定(自己的项目iOS8.0过了)。跟其他所有的Hook方案一样,会存在线上版修BUG用Hook版的代码,主干开发版用直接修改C#原方法的代码(ILRuntime也存在此种情况,只是都是“C#”代码而已)。虽说分两个版本的代码维护起来有点成本,但是如果一直只有Hook版的代码,那么主干代码在迭代的时候,容易修改了C#函数的实现,Inject后测试的时候发现又被lua给“覆盖”了!!!建议是换新包的话,就把所有的Hook版代码丢弃掉,全用主干直接修改C#函数原方法的代码。不换新包,就维护好两个版本的代码。InjectionBridgeInfo.lua是我们能替换的C#函数的LUT(look up table),我们Lua代码能替换的函数,都必须使用这个表里面的对应项。其根据InjectionBridgeEditorInfo.xml这个文件来实现自动"增量",即允许一定程度上的2份不同C#代码存运行于线上,并能被Lua替换监听(具体查看使用说明文档)。如果打包的之前,你修改了某一个函数的参数类型会导致不兼容历史记录,如果要兼容线上版的Inject代码,则最好新建一个不同名字的函数,内容拷过去,将老函数的引用重定向到新函数,而不是直接删除InjectionBridgeEditorInfo.xml这个文件(当然不考虑兼容线上版的Inject代码,则可以直接删掉这个文件,不管历史记录)。之所以提供这个增量的功能,是因为如果渠道多,打包环境会很复杂,容易遇到出多个包且包里面的C#函数有迭代的情况。“重写”C#函数的Lua函数要访问C#类对象的没有wrap进Lua环境的私有数据成员、私有方法的时候,目前只能使用静态反射。关于全Lua开发、全C#开发的问题。全Lua开发可能或多或少,会遇到lua的gc导致的性能问题。而且相对于C#这种强类型语言,Lua对于团队后期维护还是有一定的成本,不做好代码复审,相对容易变得不好维护。全C#开发配合上本方案做暖更新,修修BUG,容易遇到jit的限制,无法像全Lua或者全ILRuntime开发那么灵活的新增功能模块,不方便不换包迭代功能。故建议业务逻辑相关的都可以放可热更新的脚本层,比如角色养成,UI界面逻辑等。模块化高,重用率高,性能要求高,稳定性强的模块都可以封装到C#,比如战斗系统相关的模块,此时脚本层做好粘合剂的本职工作,搭搭积木就好。为何ILRuntime出来了,还在完善ToLua?因为ToLua相对稳定,经过了大量的线上项目验证躺坑,ILRuntime还需作者和社区花费时间和精力去完善一些细节,大幅减少其github上的issue。现使用ToLua1.0.8最新版插件已有一个商业mmo手游,Android、iOS双平台线上运行了2个月左右,iOS包也过了好几个!

C#代码:
//简单示例(ToLua demo中的例子Assets/ToLua/Examples/TestInjection/)。
using System.Collections;
using UnityEngine;

public class BaseTest
{
    private int propertyTest;

    public virtual int TestRef(ref int count)
    {
        Debug.Log("CS:Base TestRef");
        ++count;

        return 1;
    }

    public virtual int PropertyTest
    {
        get
        {
            Debug.Log("CS: Base PropertyTestGet");
            return propertyTest;
        }
        set
        {
            Debug.Log("CS: Base PropertyTestSet");
            propertyTest = value;
        }
    }
}

public class ToLuaInjectionTest : BaseTest
{
    private int propertyTest;

    public ToLuaInjectionTest()
    {
        Debug.Log("CS:Constructor Test");
    }

    public override int PropertyTest
    {
        get
        {
            Debug.Log("CS:PropertyTestGet");
            return propertyTest;
        }
        set
        {
            Debug.Log("CS:PropertyTestSet");
            propertyTest = value;
        }
    }

    public override int TestRef(ref int count)
    {
        Debug.Log("CS:Override TestRef");
        ++count;

        return 2;
    }

    public void TestOverload(int param1, bool param2)
    {
        Debug.Log("CS:TestOverload");
    }

    public void TestOverload(int param1, ref bool param2)
    {
        Debug.Log("CS:TestOverload");
        param2 = !param2;
    }

    public IEnumerator TestCoroutine(float delay)
    {
        Debug.Log("CS:TestCoroutine Run");
        yield return new WaitForSeconds(delay);
        Debug.Log("CS:TestCoroutine End");
    }
}
暖更新lua代码(此处不够完整,仅做示例用,完整代码路径):
local ToLuaInjectionTestInjector = {}

ToLuaInjectionTestInjector[".ctor"] = function()
        -- Only After Does Matter
        return function(self)
                print("Lua Inject Constructor")
        end, LuaInterface.InjectType.After
        -------------------------------------------------------
        --return function(self)
        --        print("Lua Inject Constructor")
        --end, LuaInterface.InjectType.Before
        -------------------------------------------------------
end

ToLuaInjectionTestInjector.set_PropertyTest = function()
        return function (self, value)
                print("Lua Inject Property set :" .. value)
        end, LuaInterface.InjectType.After
        -------------------------------------------------------
        --return function (self, value)
        --        print("Lua Inject Property set :")
        --        return {3}
        --end, LuaInterface.InjectType.Replace
        -------------------------------------------------------
end

ToLuaInjectionTestInjector.get_PropertyTest = function()
        return function (self)
                print("Lua Inject Property get :")
        end, LuaInterface.InjectType.After
        -------------------------------------------------------
        --return function (self)
        --        print("Lua Inject Property get :")
        --end, LuaInterface.InjectType.Before
        -------------------------------------------------------
        --return function (self)
        --        print("Lua Inject Property get :")
        --        return 2
        --end, LuaInterface.InjectType.Replace
        -------------------------------------------------------
end

ToLuaInjectionTestInjector.TestRef = function()
        --return function (self, count)
        --        print("Lua Inject TestRef ")
        --        count = 10
        --        return { count , 3}
        --end, LuaInterface.InjectType.After
        -------------------------------------------------------
        --return function (self, count)
        --        print("Lua Inject TestRef ")
        --        count = 10
        --        return { count , 3}
        --end, LuaInterface.InjectType.ReplaceWithPreInvokeBase
        -------------------------------------------------------
        return function (self, count)
                print("Lua Inject TestRef ")
                count = 10
                return { count , 3}
        end, LuaInterface.InjectType.ReplaceWithPostInvokeBase
        -------------------------------------------------------
        --return function (self, count)
        --        print("Lua Inject TestRef ")
        --        count = 10
        --        return { count , 3}
        --end, LuaInterface.InjectType.Replace
        -------------------------------------------------------
        --return function (self, count)
        --        print("Lua Inject TestRef ")
        --        count = 10
        --        return { count , 3}
        --end, LuaInterface.InjectType.Before
        -------------------------------------------------------
end

ToLuaInjectionTestInjector.TestOverload_int_bool = function()
        return function (self, count, state)
                print("Lua Inject TestOverload_int_bool " .. tostring(state))
        end, LuaInterface.InjectType.After
end

ToLuaInjectionTestInjector["TestOverload_int_bool&"] = function()
        --return function (self, param1, param2)
        --        print("Lua Inject TestOverload_int_bool& ")
        --        return {false}
        --end, LuaInterface.InjectType.After
        -------------------------------------------------------
        --return function (self, param1, param2)
        --        print("Lua Inject TestOverload_int_bool& ")
        --        return {false}
        --end, LuaInterface.InjectType.Before
        -------------------------------------------------------
        return function (self, param1, param2)
                print("Lua Inject TestOverload_int_bool& ")
                return {false}
        end, LuaInterface.InjectType.Replace
        -------------------------------------------------------
end

ToLuaInjectionTestInjector.TestCoroutine = function()
        return function (self, delay, coroutineState)
                print("Lua Inject TestCoroutine " .. coroutineState)
        end, LuaInterface.InjectType.After
        -------------------------------------------------------
        --return function (self, delay)
        --        return WrapLuaCoroutine(function()
        --                print("Lua Inject TestCoroutine Pulse" .. delay)
        --                return false
        --        end)
        --end, LuaInterface.InjectType.Replace
        -------------------------------------------------------
        --return function (self, delay)
        --        local state = true
        --        local cor
        --        local function StartLuaCoroutine()
        --                if cor == nil then
        --                        cor = coroutine.start(function()
        --                                print("Lua Coroutine Before")
        --                                coroutine.wait(delay)
        --                                state = false
        --                                print("Lua Coroutine After")
        --                        end)
        --                end
        --        end
        --
        --        return WrapLuaCoroutine(function()
        --                StartLuaCoroutine()
        --                return state
        --        end)
        --end, LuaInterface.InjectType.Replace
        -------------------------------------------------------
end

--InjectByName("ToLuaInjectionTest", ToLuaInjectionTestInjector)
InjectByModule(ToLuaInjectionTest, ToLuaInjectionTestInjector)上述例子代码无法执行,需要查看详细效果的请移步ToLua demo,仅仅用做简单介绍举例。
发表于 2021-8-13 14:06 | 显示全部楼层
我是来赞标题的
发表于 2021-8-13 14:14 | 显示全部楼层
大佬流弊
 楼主| 发表于 2021-8-13 14:21 | 显示全部楼层
这么少评论?
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 08:28 , Processed in 0.171003 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表