在Tolua中实现Xlua IL注入的热更方式

4 篇文章 0 订阅
3 篇文章 0 订阅

比如你有这么一个充值的方法

public void GetMoney(int money) {
        Debug.LogError(string.Format("充值了{0} 万元", money));
}

在ToDo之前我们来了解下Xlua热更的原理,很简单,大概就是在开始的时候加入了一个hotfix委托检测的注入,我这里就用C#代码表示了

public void GetMoney(int money) {
        if(Hotfix_GetMoney != null) return Hotfix_GetMoney(this,money);
        Debug.LogError(string.Format("充值了{0} 万元", money));
}

如果这个function出了问题,比如临时需要关闭充值入口,那么在Lua层会利用xlua.hotfix把这个LuaFuntion定位到Hotfix_GetMoney 中

 

Let's Do That ! 

注:本文实现方式与Xlua有所不同

首先,加入Hotfix特性

[AttributeUsage(AttributeTargets.All)]
public class HotFix : Attribute
{
}

然后加入Hotfix Class名单设置支持

namespace M.LuaHotFix {
    public class HotFixSetting
    {
        public static List<string> whiteClass = new List<string>{
             _GT(typeof(MyTest)),
        };
        public static string _GT(Type type)
        {
            return type.FullName;
        }
    }
}

添加IL注入菜单,搜索Dll查找Hotfix的方法

        public static void Inject()
        {
            //正在编译不允许注入
            if (Application.isPlaying || EditorApplication.isCompiling)
            {
                Debug.LogError("You need stop play mode or wait compiling finished");
                return;
            }

            var readerParameters = new ReaderParameters { ReadSymbols = true };
            var assembly = AssemblyDefinition.ReadAssembly(AssemblyPath, readerParameters);
            var module = assembly.MainModule;
            //遍历所有的类型
            foreach (var type in module.Types)
            {
                var needInjectAttr = typeof(HotFix).FullName;
                bool needInject = type.CustomAttributes.Any(typeAttribute => typeAttribute.AttributeType.FullName == needInjectAttr);
                //进一步判断是否在白名单中
                if (!needInject)
                {
                    if (HotFixSetting.whiteClass.Any(className => className == type.FullName))
                        needInject = true;
                    else
                        continue;
                }
                //遍历所有的method,进行注入
                foreach (var method in type.Methods)
                {
                    if (method.IsConstructor || method.IsGetter || method.IsSetter || !method.IsPublic)
                        continue;
                    if (!method.CustomAttributes.Any(typeAttribute => typeAttribute.AttributeType.FullName == needInjectAttr))
                        continue;
                    InjectMethod(module,type,method);
                }
            }
            assembly.Write(AssemblyPath, new WriterParameters { WriteSymbols = true });
            Debug.Log("InjectTool Inject  Success!");
        }

之后就是注入IL代码了,我们在整个方法执行前做一个是否需要Hotfix的检测

        private static void InjectMethod(ModuleDefinition module,TypeDefinition type, MethodDefinition method)
        {
            var objType = module.ImportReference(typeof(System.Object));
            //获得hotfix方法的引用
            var hotFixDetector =
                module.ImportReference(typeof(HotFixManager).GetMethod("HotFixDetector", new[] { typeof(string), typeof(string), typeof(object[]) }));
           module.ImportReference(typeof(HotFixManager).GetMethod("HotFixDetector", new[] { typeof(string), typeof(string), typeof(object[]) }));

            var insertPoint = method.Body.Instructions[0];
            var ilProcessor = method.Body.GetILProcessor();
            var paramCount = method.Parameters.Count + (method.IsStatic ? 0 : 1);

            //开始注入
            ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Nop));
            ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Ldstr, type.FullName));
            ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Ldstr, method.Name));
            ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Ldc_I4, paramCount));
            ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Newarr, objType));
            //将原本的参数压入函数堆栈
            for (int i = 0; i < paramCount; i++) {
                ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Dup));
                ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Ldc_I4,i));
                if (i < 3) ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(opLdargList[i]));
                else if (i < 256) ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(opLdargList[4], (byte)i));
                else ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(opLdargList[4], (short)i));
                TypeReference paramType;
                if (method.IsStatic)
                {
                    paramType = method.Parameters[i].ParameterType;
                }
                else
                {
                    paramType = (i == 0) ? type : method.Parameters[i - 1].ParameterType;
                }
                if (paramType.IsValueType)
                {
                    ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Box, paramType));
                }
                ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Stelem_Ref));
            }
            //Call Hotfix 拦截原方法
            ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Call, hotFixDetector));
            ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Brfalse, insertPoint));
            ilProcessor.InsertBefore(insertPoint, ilProcessor.Create(OpCodes.Ret));
        }

然后接Tolua,这个不用赘述

        public static bool HotFixDetector(string classFullName, string funcName, object[] args)
        {
            var luaState = LuaManager.luaState;
            LuaTable luaTable = RequireLuaFile("HotFix/" + Regex.Replace(classFullName, "[.]", "|"));
            if (luaTable == null)return false;
            LuaFunction func = luaTable.GetLuaFunction(funcName);
            if (func == null) return false;
            func.BeginPCall();
            foreach (var v in args)
                func.Push(v);
            func.PCall();
            func.EndPCall();
            return true;
        }

执行原本的代码是这样的:

注入了lua代码之后是

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值