比如你有这么一个充值的方法
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代码之后是