|
xLua地址:传送门
Xlua是啥?
2016年 腾讯推出的 一种 unity下 lua 编成的解决方案
基本概念介绍:
1.模块
模块就是一个 程序库,可以通过 require 加载,得到了一个表示 table的全局变量
这个table 就像一个命名空间,他的内容就是模块中导出的所有东西,比如:函数和常量
2.require 函数
Lua 提供了一个名为 require 的函数来加载模块。
执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量
将模块中旧的函数替换成新的函数,这个新的函数可以放到一个Lua文件
3.upvalue与闭包
Lua函数可以被当成参数传递,可以被当成结果返回,在函数体中仍然可以定义内嵌函数
Lua闭包是Lua函数生成的数据对象。每个闭包可以有一个upvalue值,或者多个闭包共享一个upvalue数值
(如果函数F2定义在函数F1中,那么函数F2为F1的内嵌函数,F1为F2的外包函数)
内嵌函数可以访问外包函数已经创建的局部变量,而这些局部变量则被称为该内嵌函数的外部局部变量(或者upvalue)
4.require实现热更新
Lua 的 require(modelname) 把一个lua文件加载存放到 package.load[modelname]
当我们加载一个模块的时候,会先判断是否在package.loaded中已存在,如果存在就直接返回,不存在才加载,防止重复加载
通过AddLoader可以注册个回调,该回调参数是字符串
lua代码调用require时,参数将会透传给回调,回调中可以根据这个参数取加载指定文件
返回一个byte数组,如果为空则表示该loader找不到,否则则为lua文件内容- publicclassCustomLoader:Monobehaviour{LuaEnv luaenv =null;voidStart(){
- luaenv =newLuaEnv();
- luaenv.AddLoader((refstring filename)=>{return xxxx;});}}
复制代码 5.xLua使用流程
如图所示:
流程:
1.xLua文件配置:通过对C#的类与函数设置Hotfix标签。来标识需要支持热更的类和函数
a.打标签:
xLua用白名单来指明生成哪些代码,而白名单通过attribute来配置
(比如想从lua调用c#的某个类,希望生成适配代码,可以加一个 LuaCallCSharp 标签)
注意:会在il2cpp下增加不少代码量,不建议使用- [LuaCallCSharp]public classA
- {}
复制代码 b.静态列表
有时候不能给一个类型打标签(比如系统的API,没源码的库,或者实例化的泛型类型)
可以在一个静态类里声明一个静态字段,该字段的类型除 BlackList 和 AdditionalProperties 之外只要实现了 Ienumerable 就可以了- [LuaCallCSharp]
- public static List<Type> my_lua_call_cs_list = new List<Type>()
- {
- typeof(GameObject),
- typeof(Dictionary<string, int>)
- };
复制代码 c.动态列表
使用动态列表的方式,声明一个静态属性
比如对某命名空间下的所有类配置到 Hotfix 列表 or 全注入+排除特定 or 放到目录通过正则生成一个类列表等等- publicstatic List<Type> by_property
- {get{return(from type int Assembly.Load("Assembly-CSharp").GetTypes()where type.Namespace =="XXX"select type).ToList();}}
复制代码 注意:
上线后很难预测那些地方有bug,大部分时候需要把大多数类都囊括在内,然后把一些不需要通过热更新修改bug的部分排除在外(第三方库啥的),然后随着后续的迭代把日趋稳定的模块排除在外。
2.加载Lua文件
有三种方式
a.执行字符串
b.加载lua文件
c.自定义Loader(一般都用这种)
3.连接lua脚本与C#函数:xLua中有两种方式来实现Lua调用CS种德方法,一种是反射调用,一种是生成适配的代码
a.反射调用- [MonoPInvokeCallback(typeof(LuaCSFunction))]staticintFixCSFuction(RealStatePtr L){try{ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);int idx = LuaAPI.xlua_tointeger(L, LuaAPI.xlua_upvalueindex(1));//获取闭包中的 upvalue 值LuaCSFunction func =(LuaCSFunction)translator.GetFixCSFunction(idx);// 获取对应的 LuaCSharpreturnfunc(L);// 执行}catch(System.Exception e){return LuaAPI.luaL_error(L,"c# exception in FixCSFunction : "+ e);}}
复制代码 b.生成适配器代码(Generate Code)
在xLua中生成适配代码后会在Gen目录生成代码
根据C#类中需要支持热更的方法,生成对应的委托方法。
注册函数:- // 以偏向减少代码段的方式生成代码publicstaticvoidRegisterFunc(RealStatePtr L,int idx,string name,LuaCSFunction func){
- idx =abs_idx(LuaAPI.lua_gettop(L), idx);// idx 就是指CLS_IDX,也就是SetCSTable设置的值
- LuaAPI.xlua_pushasciistring(L, name);// 压入方法名
- LuaAPI.lua_pushstdcallcfunction(L, func);// 压入c# wrapper
- LuaAPI.lua_rawset(L, idx);}
复制代码- public static void __Register(RealStatePtr L)
- {
- ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
- System.Type type = typeof(GameGlobal);
- //注册成员方法
- Utils.BeginObjectRegister(type, L, translator, 0, 0, 0, 0);
-
- Utils.EndObjectRegister(type, L, translator, null, null,
- null, null, null);
- //注册静态方法
- Utils.BeginClassRegister(type, L, __CreateInstance, 2, 26, 0);
- //注册回调
- Utils.RegisterFunc(L, Utils.CLS_IDX, "QuitGame", _m_QuitGame_xlua_st_);
-
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "CoroutineTask", _g_get_CoroutineTask);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "TimeTask", _g_get_TimeTask);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "GameUpdater", _g_get_GameUpdater);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "Color", _g_get_Color);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "GameState", _g_get_GameState);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "GameModule", _g_get_GameModule);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "ResourcesMng", _g_get_ResourcesMng);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "Resources", _g_get_Resources);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "EVDispatcher", _g_get_EVDispatcher);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "UIModule", _g_get_UIModule);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "UIConfig", _g_get_UIConfig);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "UIController", _g_get_UIController);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "RedPoint", _g_get_RedPoint);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "UILayer", _g_get_UILayer);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "Data", _g_get_Data);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "Net", _g_get_Net);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "Version", _g_get_Version);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "SDK", _g_get_SDK);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "HotUpdate", _g_get_HotUpdate);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "DB", _g_get_DB);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "Sound", _g_get_Sound);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "HotFix", _g_get_HotFix);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "HeartPingManager", _g_get_HeartPingManager);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "ApplicateQuit", _g_get_ApplicateQuit);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "ApplicationPaused", _g_get_ApplicationPaused);
- Utils.RegisterFunc(L, Utils.CLS_GETTER_IDX, "ApplicationFocused", _g_get_ApplicationFocused);
- Utils.EndClassRegister(type, L, translator);
- }
- ...
复制代码 3.热更脚本注入:在C#脚本编译结束后,使用Mono提供的一套C#的API函数,对已经编译过的.Net体系生成的DLL文件进行修改
xLua对dll注入一些判断条件式来完成
Lua调用的行为很简单,就是检查对应类静态字段是否有 DelegateBridge 对象
(非常长的)源码如下:- boolinjectMethod(MethodDefinition method,HotfixFlagInTool hotfixType){var type = method.DeclaringType;bool isFinalize =(method.Name =="Finalize"&& method.IsSpecialName);MethodReference invoke =null;//__Gen_Delegate_Imp 方法引用int param_count = method.Parameters.Count +(method.IsStatic ?0:1);if(!findHotfixDelegate(method,out invoke, hotfixType))// 根据返回值和参数个数类型,查找对应的委托方法{Error("can not find delegate for "+ method.DeclaringType +"."+ method.Name +"! try re-genertate code.");returnfalse;}if(invoke ==null){thrownewException("unknow exception!");}#if XLUA_GENERAL
- invoke = injectAssembly.MainModule.ImportReference(invoke);#else
- invoke = injectAssembly.MainModule.Import(invoke);#endifFieldReference fieldReference =null;//插入的类静态字段,用来标记是否有对应的Lua注入VariableDefinition injection =null;//方法中的变量顶一bool isIntKey = hotfixType.HasFlag(HotfixFlagInTool.IntKey)&&!type.HasGenericParameters && isTheSameAssembly;//isIntKey = !type.HasGenericParameters;if(!isIntKey){
- injection =newVariableDefinition(invoke.DeclaringType);//新建变量,加入方法体的变量组中
- method.Body.Variables.Add(injection);//获取这个方法对应的委托名,因为有重载方法存在//所以之前已经注入过的方法会在这边获取时计数+1//比如第一个重载获取的是__Hotfix0,那么下一个重载会是__Hotfix1,判断是否注入就是对应FieldReferencevar luaDelegateName =getDelegateName(method);if(luaDelegateName ==null){Error("too many overload!");returnfalse;}//创建对应的静态Field名字 就是上面收取道德luaDelegateNameFieldDefinition fieldDefinition =newFieldDefinition(luaDelegateName, Mono.Cecil.FieldAttributes.Static | Mono.Cecil.FieldAttributes.Private,
- invoke.DeclaringType);
- type.Fields.Add(fieldDefinition);
- fieldReference = fieldDefinition.GetGeneric();}bool ignoreValueType = hotfixType.HasFlag(HotfixFlagInTool.ValueTypeBoxing);//IL插入位置,目前定位的是方法的第一行var insertPoint = method.Body.Instructions[0];//获取IL处理器var processor = method.Body.GetILProcessor();//构造函数的处理逻辑先跳过这边不做分析if(method.IsConstructor){
- insertPoint =findNextRet(method.Body.Instructions, insertPoint);}
- Dictionary<Instruction, Instruction> originToNewTarget =newDictionary<Instruction,Instruction>();
- HashSet<Instruction> noCheck =newHashSet<Instruction>();while(insertPoint !=null){Instruction firstInstruction;if(isIntKey){
- firstInstruction = processor.Create(OpCodes.Ldc_I4, bridgeIndexByKey.Count);
- processor.InsertBefore(insertPoint, firstInstruction);
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Call, hotfixFlagGetter));}else{//创建第一条 IL 语句,获取类的静态Field压入方法栈用(之前luaDelegateName获取的字段)
- firstInstruction = processor.Create(OpCodes.Ldsfld, fieldReference);//插入 insertPoint 之前
- processor.InsertBefore(insertPoint, firstInstruction);//创建并插入IL,获取栈顶的值并压入到对应的变量中(之前创建的新建变量)
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Stloc, injection));//创建并插入 IL,压入变量体中的值到栈
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));}//创建跳转语句,为false时候直接跳转到insertPoint//OpCodes.Brfalse看起来是布尔值判断,其实也会判断是否为nullvar jmpInstruction = processor.Create(OpCodes.Brfalse, insertPoint);
- processor.InsertBefore(insertPoint, jmpInstruction);if(isIntKey){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldc_I4, bridgeIndexByKey.Count));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Call, delegateBridgeGetter));}else{//创建并插入IL,再次压入变量的值,因为上面判断之后,栈顶的值就会被弹出
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));}//后面就先不分析了。。。for(int i =0; i < param_count; i++){if(i < ldargs.Length){
- processor.InsertBefore(insertPoint, processor.Create(ldargs[i]));}elseif(i <256){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldarg_S,(byte)i));}else{
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldarg,(short)i));}if(i ==0&&!method.IsStatic && type.IsValueType){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldobj, type));}if(ignoreValueType){TypeReference paramType;if(method.IsStatic){
- paramType = method.Parameters[i].ParameterType;}else{
- paramType =(i ==0)? type : method.Parameters[i -1].ParameterType;}if(paramType.IsValueType){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Box, paramType));}}}
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Call, invoke));if(!method.IsConstructor &&!isFinalize){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ret));}if(!method.IsConstructor){break;}else{
- originToNewTarget[insertPoint]= firstInstruction;
- noCheck.Add(jmpInstruction);}
- insertPoint =findNextRet(method.Body.Instructions, insertPoint);}if(method.IsConstructor){fixBranch(processor, method.Body.Instructions, originToNewTarget, noCheck);}if(isFinalize){if(method.Body.ExceptionHandlers.Count ==0){thrownewInvalidProgramException("Finalize has not try-catch? Type :"+ method.DeclaringType);}
- method.Body.ExceptionHandlers[0].TryStart = method.Body.Instructions[0];}if(isIntKey){
- bridgeIndexByKey.Add(method);}returntrue;}boolinjectGenericMethod(MethodDefinition method,HotfixFlagInTool hotfixType){//如果注入的是xlua所在之外的Assembly的话,不支持该方式if(!isTheSameAssembly){returntrue;}var type = method.DeclaringType;bool isFinalize =(method.Name =="Finalize"&& method.IsSpecialName);bool isIntKey = hotfixType.HasFlag(HotfixFlagInTool.IntKey)&&!type.HasGenericParameters;//isIntKey = !type.HasGenericParameters;FieldReference fieldReference =null;VariableDefinition injection =null;if(!isIntKey){var luaDelegateName =getDelegateName(method);if(luaDelegateName ==null){Error("too many overload!");returnfalse;}FieldDefinition fieldDefinition =newFieldDefinition(luaDelegateName, Mono.Cecil.FieldAttributes.Static | Mono.Cecil.FieldAttributes.Private,
- delegateBridgeType);
- type.Fields.Add(fieldDefinition);
- fieldReference = fieldDefinition.GetGeneric();}
- injection =newVariableDefinition(delegateBridgeType);
- method.Body.Variables.Add(injection);int param_start = method.IsStatic ?0:1;int param_count = method.Parameters.Count + param_start;var insertPoint = method.Body.Instructions[0];var processor = method.Body.GetILProcessor();if(method.IsConstructor){
- insertPoint =findNextRet(method.Body.Instructions, insertPoint);}
- Dictionary<Instruction, Instruction> originToNewTarget =newDictionary<Instruction,Instruction>();
- HashSet<Instruction> noCheck =newHashSet<Instruction>();while(insertPoint !=null){Instruction firstInstruction;Instruction jmpInstruction;if(isIntKey){
- firstInstruction = processor.Create(OpCodes.Ldc_I4, bridgeIndexByKey.Count);
- processor.InsertBefore(insertPoint, firstInstruction);
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Call, hotfixFlagGetter));
- jmpInstruction = processor.Create(OpCodes.Brfalse, insertPoint);
- processor.InsertBefore(insertPoint, jmpInstruction);
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldc_I4, bridgeIndexByKey.Count));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Call, delegateBridgeGetter));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Stloc, injection));}else{
- firstInstruction = processor.Create(OpCodes.Ldsfld, fieldReference);
- processor.InsertBefore(insertPoint, firstInstruction);
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Stloc, injection));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));
- jmpInstruction = processor.Create(OpCodes.Brfalse, insertPoint);
- processor.InsertBefore(insertPoint, jmpInstruction);}
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Callvirt, invokeSessionStart));TypeReference returnType = method.ReturnType;bool isVoid = returnType.FullName =="System.Void";int outCout =0;for(int i =0; i < param_count; i++){if(i ==0&&!method.IsStatic){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldarg_0));if(type.IsValueType){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldobj, method.DeclaringType.GetGeneric()));}
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Callvirt, inParam.MakeGenericMethod(method.DeclaringType.GetGeneric())));}else{var param = method.Parameters[i - param_start];if(param.ParameterType.IsByReference){
- outCout++;}if(!param.IsOut){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));if(i < ldargs.Length){
- processor.InsertBefore(insertPoint, processor.Create(ldargs[i]));}elseif(i <256){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldarg_S,(byte)i));}else{
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldarg,(short)i));}var paramType = param.ParameterType;if(param.ParameterType.IsByReference){
- paramType =((ByReferenceType)paramType).ElementType;if(paramType.IsValueType || paramType.IsGenericParameter){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldobj, paramType));}else{
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldind_Ref));}}if(i == param_count -1&& param.CustomAttributes.Any(ca => ca.AttributeType.FullName =="System.ParamArrayAttribute")){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Callvirt, inParams.MakeGenericMethod(((ArrayType)paramType).ElementType)));}else{
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Callvirt, inParam.MakeGenericMethod(paramType)));}}}}int outStart =(isVoid ?0:1);
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldc_I4, outCout + outStart));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Callvirt, functionInvoke));int outPos = outStart;for(int i =0; i < method.Parameters.Count; i++){if(method.Parameters[i].ParameterType.IsByReference){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldc_I4, outPos));int arg_pos = param_start + i;if(arg_pos < ldargs.Length){
- processor.InsertBefore(insertPoint, processor.Create(ldargs[arg_pos]));}elseif(arg_pos <256){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldarg_S,(byte)arg_pos));}else{
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldarg,(short)arg_pos));}
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Callvirt, outParam.MakeGenericMethod(((ByReferenceType)method.Parameters[i].ParameterType).ElementType)));
- outPos++;}}
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ldloc, injection));if(isVoid){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Callvirt, invokeSessionEnd));}else{
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Callvirt, invokeSessionEndWithResult.MakeGenericMethod(returnType)));}if(!method.IsConstructor &&!isFinalize){
- processor.InsertBefore(insertPoint, processor.Create(OpCodes.Ret));}if(!method.IsConstructor){break;}else{
- originToNewTarget[insertPoint]= firstInstruction;
- noCheck.Add(jmpInstruction);}
- insertPoint =findNextRet(method.Body.Instructions, insertPoint);}if(method.IsConstructor){fixBranch(processor, method.Body.Instructions, originToNewTarget, noCheck);}if(isFinalize){
- method.Body.ExceptionHandlers[0].TryStart = method.Body.Instructions[0];}if(isIntKey){
- bridgeIndexByKey.Add(method);}returntrue;}publicvoidOutputIntKeyMapper(Stream output){using(StreamWriter writer =newStreamWriter(output)){
- writer.WriteLine("return {");var data = bridgeIndexByKey
- .Select((md, idx)=>new{ Method = md, Index = idx}).GroupBy(info => info.Method.DeclaringType).ToDictionary(group=>group.Key,group=>{returngroup.GroupBy(info => info.Method.Name).ToDictionary(group_by_name => group_by_name.Key, group_by_name => group_by_name.Select(info => info.Index.ToString()).ToArray());});foreach(var kv in data){
- writer.WriteLine(" [""+ kv.Key.FullName.Replace('/','+')+""] = {");foreach(var kv2 in kv.Value){
- writer.WriteLine(" [""+ kv2.Key +""] = {");
- writer.WriteLine(" "+string.Join(",", kv2.Value));
- writer.WriteLine(" },");}
- writer.WriteLine(" },");}
- writer.WriteLine("}");}}
复制代码 4.执行热更脚本:通过脚本修改C#带有标签的类中的静态变量,把代码的执行路径修改到Lua脚本中
在完成生成代码和注入猴,只要在Lua中调用 xlua.hotfix 或 util.hotfix_ex 方法,就可以实现 c# 代码热更新- -- hotfix 和 hotfix_ex 的区别就是是否可以调用原 c# 代码,其实ex的实现也是调用了hotfix
- xlua.hotfix =function(cs, field, func)if func ==nilthen func =falseendlocal tbl =(type(field)=='table')and field or{[field]= func}-- 遍历需要 hotfix 的代码,key是方法名,v是对应的funcfor k,v inpairs(tbl)dolocal cflag =''if k =='.ctor'then
- clfag ='c'
- k ='ctor'endlocal f =type(v)=='fucntion'and v ornil-- 调用 access 方法
- xlua.acces(cs, cflag..'__Hotfix0_'..k, f)--at least one-- 添加重载方法pcall(function()for i =1,99do
- xlua.access(cs, cflag..'__Hotfix'..i..'_'..k, f)endend)end-- 设置私有访问
- xlua.private_accessible(cs)end
复制代码 其中额外的东西:
a).xlua.access 对应的c#代码是xLuaAccess代码处理
代码对应 XLua.StaticLuaCallbacks.cs 里面的 XLuaAccess() 方法- [MonoPInvokeCallback(typeof(LuaCSFunction))]publicstaticintXLuaAccess(RealStatePtr L){try{ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);Type type =getType(L, translator,1);object obj =null;if(type ==null&& LuaAPI.lua_type(L,1)== LuaTypes.LUA_TUSERDATA){
- obj = translator.SafeGetCSObj(L,1);if(obj ==null){return LuaAPI.luaL_error(L,"xlua.access, #1 parameter must a type/c# object/string");}
- type = obj.GetType();}if(type ==null){return LuaAPI.luaL_error(L,"xlua.access, can not find c# type");}//将cflag..'__Hotfix0_'..k 转为fieldName,这个字段就是之前Inject时候创建的类的静态字段名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));return0;}var prop = type.GetProperty(fieldName, bindingFlags);if(prop !=null){//修改当前函数名称
- prop.SetValue(obj, translator.GetObject(L,3, prop.PropertyType),null);return0;}}else{var field = type.GetField(fieldName, bindingFlags);if(field !=null){
- translator.PushAny(L, field.GetValue(obj));return1;}var prop = type.GetProperty(fieldName, bindingFlags);if(prop !=null){
- translator.PushAny(L, prop.GetValue(obj,null));return1;}}return LuaAPI.luaL_error(L,"xlua.access, no field "+ fieldName);}catch(Exception e){return LuaAPI.luaL_error(L,"c# exception in xlua.access: "+ e);}}
复制代码 b).创建修改函数对象
代码对应 XLua.ObjectTranslator.cs 里面的 CreateDelegateBridge () 方法- publicobjectCreateDelegateBridge(RealStatePtr L,Type delegateType,int idx){
- LuaAPI.lua_pushvalue(L, idx);
- LuaAPI.lua_rawget(L, LuaIndexes.LUA_REGISTRYINDEX);if(!LuaAPI.lua_isnil(L,-1)){int referenced = LuaAPI.xlua_tointeger(L,-1);
- LuaAPI.lua_pop(L,1);if(delegate_bridges[referenced].IsAlive){if(delegateType ==null){return delegate_bridges[referenced].Target;}DelegateBridgeBase exist_bridge = delegate_bridges[referenced].Targetas DelegateBridgeBase;Delegate exist_delegate;if(exist_bridge.TryGetDelegate(delegateType,out exist_delegate)){return exist_delegate;}else{
- exist_delegate =getDelegate(exist_bridge, delegateType);
- exist_bridge.AddDelegate(delegateType, exist_delegate);return exist_delegate;}}}else{
- LuaAPI.lua_pop(L,1);}//push idx对应的值,idx对应的值是Lua中的function
- LuaAPI.lua_pushvalue(L, idx);//获取应用的引用idint reference = LuaAPI.luaL_ref(L);//再次压入idx对应的值,idx对应的值是Lua中的函数
- LuaAPI.lua_pushvalue(L, idx);//压入方法对应的引用id
- LuaAPI.lua_pushnumber(L, reference);//将栈顶的两个值存入全局变量表中,方便查询是否已经在lua中缓存
- LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX);DelegateBridgeBase bridge;try{#if (UNITY_EDITOR || XLUA_GENERAL) && !NET_STANDARD_2_0if(!DelegateBridge.Gen_Flag){
- bridge = Activator.CreateInstance(delegate_birdge_type,newobject[]{ reference, luaEnv })as DelegateBridgeBase;}else#endif{//创建 DeleagteBridge,reference 对应了 lua 中修复的lua函数,在 Inject 时候 call 的方法会使用到这个函数
- bridge =newDelegateBridge(reference, luaEnv);}}catch(Exception e){
- LuaAPI.lua_pushvalue(L, idx);
- LuaAPI.lua_pushnil(L);
- LuaAPI.lua_rawset(L, LuaIndexes.LUA_REGISTRYINDEX);
- LuaAPI.lua_pushnil(L);
- LuaAPI.xlua_rawseti(L, LuaIndexes.LUA_REGISTRYINDEX, reference);throw e;}if(delegateType ==null){
- delegate_bridges[reference]=newWeakReference(bridge);return bridge;}try{var ret =getDelegate(bridge, delegateType);
- bridge.AddDelegate(delegateType, ret);
- delegate_bridges[reference]=newWeakReference(bridge);return ret;}catch(Exception e){
- bridge.Dispose();throw e;}}
复制代码 在调用hotfix猴,对应的修复类的静态字段会被设置成对应的DelegateBridge对象
c#执行到对应的被 xLua热更修复的代码时,会先执行注入的IL代码,检查是否有DelegateBridge,就会实际执行里面的方法,这样就执行了到了lua修复方法,实现了热更新 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|