[ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能

附有道云笔记排版文档:自定义tolua加载ab模拟机制,完美解决?..
链接:http://note.youdao.com/noteshare?id=c6da58f8245888d33013b7c2aef6bc6c

之前用的AB模拟加载机制,自定义写了一套加载流程,但是无法调试lua或者或者概率性能调试,因此写了一个新的加载流程框架完美解决了AssetBundle模拟调试(修改lua代码后不需要实时打Bundle就能立刻看到lua代码的修改效果)但是tolua不能debug的问题

原理:lua文件是通过路径来加载的,

  1. 设置好对应的加载路径(如:AddSearchPath)
  2. 设置好对应的加载方法(如:assetbundle)
  3. 设置好对应的加载方式(如:dofile(),dostring())

核心思路:

  1. 保证需要加载的文件全部加载完成
  2. 保证需要加载的文件启动入口准确

原框架逻辑梳理:

  1. tolua的打包机制是 提前addbundle预加载包,和后续的ab包,整合在平台的如Android.manifest
  2. tolua的预加载是通过AddBundle()到指定查询路径
#region 自定义操作行为
        /// 
        /// 添加打入Lua代码的AssetBundle
        /// 
        /// 
        public void AddBundle(string bundleName)
        {
            string url = ELUtil.DataPath + bundleName.ToLower();
            Debug.LogError(url);
            if (File.Exists(url))
            {
                var         bytes  = File.ReadAllBytes(url);
                AssetBundle bundle = AssetBundle.LoadFromMemory(bytes);
                if (bundle != null)
                {
                    bundleName = bundleName.Replace("lua/", "").Replace(".unity3d", "");
                    base.AddSearchBundle(bundleName.ToLower(), bundle);
                    Debug.LogError("true:" + bundleName);
                }
            }
        }

        #endregion
    /// 
        /// 初始化LuaBundle
        /// 
        void InitLuaBundle()
        {
            if (_luaLoader.beZip)
            {
                _luaLoader.AddBundle("lua_lua.unity3d");
                _luaLoader.AddBundle("lua_lua_math.unity3d");
                _luaLoader.AddBundle("lua_lua_debug.unity3d");
				...
            }
        }

tolua框架加载方法如果是开启zip走zip方法LuaInterface.LuaFileUtilsReadZipFile(string fileName) 否则走正常加载 File.ReadAllBytes(string fullPath);

  1. 注:zip方法就是相对路径加载方法,通过searchPath来加载
  2. 否则走全路径加载方法(绝对路径LoadFIle)
        public  byte[] ReadFile(string fileName)
        {
            if (!beZip)
            {
                string path = FindFile(fileName);
                byte[] str = null;

                if (!string.IsNullOrEmpty(path) && File.Exists(path))
                {
#if !UNITY_WEBPLAYER
                    str = File.ReadAllBytes(path);
#else
                    throw new LuaException("can't run in web platform, please switch to other platform");
#endif
                }

                return str;
            }
            else
            {
                return ReadZipFile(fileName);
            }
        }
 public virtual  byte[] ReadZipFile(string fileName)
        {
            AssetBundle zipFile = null;
            byte[] buffer = null;
            string zipName = null;

            using (CString.Block())
            {
                CString sb = CString.Alloc(256);
                sb.Append("lua");
                int pos = fileName.LastIndexOf('/');

                if (pos > 0)
                {
                    sb.Append("_");
                    sb.Append(fileName, 0, pos).ToLower().Replace('/', '_');
                    fileName = fileName.Substring(pos + 1);
                }

                if (!fileName.EndsWith(".lua"))
                {
                    fileName += ".lua";
                }

#if UNITY_5 || UNITY_5_3_OR_NEWER
                fileName += ".bytes";
#endif
                zipName = sb.ToString();
                zipMap.TryGetValue(zipName, out zipFile);
            }

            if (zipFile != null)
            {
#if UNITY_4_6 || UNITY_4_7
                TextAsset luaCode = zipFile.Load(fileName, typeof(TextAsset)) as TextAsset;
#else
                TextAsset luaCode = zipFile.LoadAsset<TextAsset>(fileName);
#endif
                if (luaCode != null)
                {
                    buffer = luaCode.bytes;
                    Resources.UnloadAsset(luaCode);
                }
            }

            return buffer;
        }

设置搜索路径 LuaFileUtils.Instance.AddSearchPath(path);

      //格式: 路径/?.lua
        public bool AddSearchPath(string path, bool front = false)
        {
            int index = searchPaths.IndexOf(path);

            if (index >= 0)
            {
                return false;
            }

            if (front)
            {
                searchPaths.Insert(0, path);
            }
            else
            {
                searchPaths.Add(path);
            }

            return true;
        }

Addbundle方法预加载走的是zip通道加载(注:addbundle之前必须保证bundle在StreamAsset文件夹中打包出来了,否则AddBundle失败)

那么我想法就是:之前为什么连不上,或者断断续续能断点

  1. 之前能加载,肯定是有socket连上了
  2. 但是因为部分tolua核心文件 require路径失败,导致逻辑无法执行,也就是无法挂上钩子也不报错

修正方案:

  1. 把tolua的lua文件全部改成自定义的加载方式(改成lua.txt文件,走我们自己写的ab渠道打包机制),这样全部tolua的自带文件和我们的代码就可以同时启动
  2. 自定义加载方法:把tolua的框架加载方法改成虚方法
    a. public virtual byte[] ReadZipFile(string fileName)
    b. 在自定义加载loader重写加载方法
    c. 我一开始的想法是tolua的lua文件用原逻辑加载,就是虚方法的zip(必须预先添加addbundle和searchpath)
        public override  byte[] ReadZipFile(string fileName)
        {
            //暂时取消tolua默认配置文件走默认加载通道过程,全部切换为.txt走AssetBundles/Lua/ELTolua 路径(全自动生成后缀)]
            //而且tolua.lua启动列表的require全部需要加前缀/eltolua/

            return hash.Contains(fileName) ?
                base.ReadZipFile(fileName) : ReadZipFileCustom(ref fileName);
            //return ReadZipFileCustom(ref fileName);
        }
	d. 原有的加载路径匹配上了就走默认加载,不然走自定义加载渠道
  #region 重写AB加载lua,但是默认tolua走原加载渠道

        public HashSet<string> hash = new HashSet<string>()
        {
            "tolua.lua",
            "misc/functions",
            "misc/misc",
            "misc/strict",
            "misc/utf8",
			...
        };
	e. 但是发现这样搞,Android.manifest会对不上啊,两套打包机制是不合理的
	f. 那么我还是走了自己的打包流程,也就是需要手动把tolua里全部lua文件改成lua.txt后缀(有批量改名软件)
        /// 
        /// 自定义Lua为 AB加载渠道 
        /// 
        /// 
        /// 
        private static byte[] ReadZipFileCustom(ref string fileName)
        {
            AssetBundle zipFile = null;
            byte[] buffer = null;
            string zipName = null;
            StringBuilder sb = StringBuilderCache.Acquire();
            int pos = fileName.LastIndexOf('/');

            if (pos > 0)
            {
                sb.Append(fileName.Substring(0, pos).ToLower());        //shit, unity5 assetbund'name must lower
                sb.Replace('/', '_');
                fileName = fileName.Substring(pos + 1);
            }

            if (!fileName.EndsWith(".lua"))
            {
                fileName += ".lua";
            }

#if UNITY_5_4_OR_NEWER
            //fileName += ".bytes";
#endif
            zipName = StringBuilderCache.GetStringAndRelease(sb);
            string assetBundleName = string.Empty;
            if (zipName.Equals(string.Empty))
            {
                assetBundleName = "lua.unity3d";
            }
            else
            {
                assetBundleName = "lua/" + zipName + ".unity3d";
            }

            TextAsset luaCode = ELAssetBundleManager.LoadAsset<TextAsset>(assetBundleName, fileName);
            if (luaCode != null)
            {
                buffer = luaCode.bytes;
                Resources.UnloadAsset(luaCode);
            }
     
            return buffer;
        }

--------------------------分隔符-------------------------------------
g. 而且写了一套自动设置lua打包后缀的方法,以免出错,只设置.lua.txt文件,其他.lua文件呢就报错提示不匹配规则(暂不考虑变体,变体只需要改打包后缀.unity3d为其他)
i.打包后的路径为:一级路径:lua.unity3d
ii.二级和多级路径为:lua/文件夹小写/文件夹小写.unity3d
iii.因为lua/.unity3d是不允许的,在加载路径规则中.和/都是文件夹分隔符的意思
AssetBundle编辑器
--------------------------分隔符-------------------------------------
h. 但是tolua的文件太过于零零散散,我就整合在AssetBundle/Lua/ElHotLua文件夹内,看起来完美,但是根据打包规则,需要把tolua内部的全部require路径加多elhotlua(不区分大小写,打包和加载机制自动变小写),也就是比如

  • i.Mathf = require “UnityEngine.Mathf”
  • ii.需要改为Mathf = require “ELHotLua.UnityEngine.Mathf”
  • iii.发现要改的地方很多,所以权衡下,把tolua完完整整拷贝到一级目录,就是AssetBundle/Lua文件夹内,就会造成真的很丑,先忍着,(因为框架更新再次替换tolua文件不这样搞会很麻烦,还需要重新挨个改引用(或许可以做匹配))
    [ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能_第1张图片
    最后打包出来的文件如下:tolua框架的lua文件加载规则是lua_XX_XX.unity3d
    [ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能_第2张图片
    我们自己写的代码全部是lua/XX_XX.unity3d
    [ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能_第3张图片
    那么这样就完美的把所有Tolua的文件和我们自己的文件一起打包了

3.打包完后,开始加载调试,发现socket连上了,逻辑正常了,但是就是不能断点
[ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能_第4张图片

为什么呢?我们来看下lua的调试文件

  • 调试规则是在启动文件中链接LuaDebug.jit(注意debug文件必须与启动文件在同一个文件夹内,否则有坑无法链接上)
    [ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能_第5张图片

  • 启动入口方法是StartDebug
    [ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能_第6张图片

  • socket的ip和端口的启动方法[ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能_第7张图片

  • 钩子程序通过xpcall(function,function)调用

    • debugger_loop()方法检测
    • _resume()协程挂起调试
  • 经过各种打印得到socket连接,但是钩子挂起始终不生效

    • 那么,到底是阻塞失败了
    • 还是没有断点呢(断点信息已经打上了啊)
  • 经过询问LuaIDE的作者K大神得知,自定义加载渠道,如果不是DoFile的话,而是DoString()是需要附加上chunkName的(疏忽没检查这个了),而且luadebugjit和启动类必须在相同路径在这里插入图片描述

经过最终测试,问题解决了,贴图证明
[ELFrameWork.AssetBundle框架]自定义加载流程和实现tolua断点功能_第8张图片

总结如下:自定义AB热更加载框架,必须完完整整(重要的事情说三次)

  • 把tolua的文件加载进来
  • 请注意是DoFile(string fileName)还是DoString(string chunk, string chunkName = “LuaState.cs”)
  • 启动文件luadebugjit.lua.txt必须和启动类main.lua.txt在相同路径
  • 注意socket端口是否被其他程序占用
  • tolua文件必须和自己写的文件一起打包(无论是AB打包还是模拟加载)

剩下的就是自定义打包流程和加载流程了,预知后事如何,请关注下篇(如何实现AssetBundlea模拟加载)

你可能感兴趣的:(【主干开发】游戏逻辑架构)