Unity toLua加载lua的流程

转载注明出处: https://blog.csdn.net/qweewqpkn/article/details/86565686


1.Unity加载Lua文件的流程

Unity中我们要加载Lua文件调用的函数是:LuaState类中的DoFile(“xxx”).我们可以看到流程是:

LuaState:DoFile()->LuaState:LoadFileBuffer()->LuaFileUtils:ReadFile()->FindFile()

public void DoFile(string fileName)
{
    byte[] buffer = LoadFileBuffer(fileName);
    fileName = LuaChunkName(fileName);
    LuaLoadBuffer(buffer, fileName);
}
byte[] LoadFileBuffer(string fileName)
{
#if UNITY_EDITOR
    if (!beStart)
    {
        throw new LuaException("you must call Start() first to initialize LuaState");
    }
#endif
    byte[] buffer = LuaFileUtils.Instance.ReadFile(fileName);

    if (buffer == null)
    {
        string error = string.Format("cannot open {0}: No such file or directory", fileName);
        error += LuaFileUtils.Instance.FindFileError(fileName);
        throw new LuaException(error);
    }

    return buffer;
}
public virtual 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 string FindFile(string fileName)
{
    if (fileName == string.Empty)
    {
        return string.Empty;
    }

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

        return fileName;
    }

    if (fileName.EndsWith(".lua"))
    {
        fileName = fileName.Substring(0, fileName.Length - 4);
    }

    string fullPath = null;

    for (int i = 0; i < searchPaths.Count; i++)
    {
        fullPath = searchPaths[i].Replace("?", fileName);

        if (File.Exists(fullPath))
        {
            return fullPath;
        }
    }

    return null;
}

最后会在searchPaths的路径中进行搜索,是否存在指定的lua文件,来进行加载与否。
那这个searchPaths变量在哪里初始化呢?
第一处是AddSearchPath:
我们获取lua中的package.path来初始化我们的搜索路径。

public void InitPackagePath()
{
    LuaGetGlobal("package");
    LuaGetField(-1, "path");
    string current = LuaToString(-1);
    string[] paths = current.Split(';');

    for (int i = 0; i < paths.Length; i++)
    {
        if (!string.IsNullOrEmpty(paths[i]))
        {
            string path = paths[i].Replace('\\', '/');
            LuaFileUtils.Instance.AddSearchPath(path);
        }
    }

    LuaPushString("");            
    LuaSetField(-3, "path");
    LuaPop(2);
}

第二处是LuaState类中的AddSearchPath:
InitLuaPath是在LuaState构造函数中调用的。

void InitLuaPath()
{
    InitPackagePath();

    if (!LuaFileUtils.Instance.beZip)
    {
#if UNITY_EDITOR
        if (!Directory.Exists(LuaConst.luaDir))
        {
            string msg = string.Format("luaDir path not exists: {0}, configer it in LuaConst.cs", LuaConst.luaDir);
            throw new LuaException(msg);
        }

        if (!Directory.Exists(LuaConst.toluaDir))
        {
            string msg = string.Format("toluaDir path not exists: {0}, configer it in LuaConst.cs", LuaConst.toluaDir);
            throw new LuaException(msg);
        }

        AddSearchPath(LuaConst.toluaDir);
        AddSearchPath(LuaConst.luaDir);
#endif
        if (LuaFileUtils.Instance.GetType() == typeof(LuaFileUtils))
        {
            AddSearchPath(LuaConst.luaResDir);
        }
    }
}

2.lua中加载其他lua文件

lua中我们是使用require来进行加载其他lua文件的,首先require的伪代码实现如下:

function require(name)
    if not package.loaded[name] then
        local loader = findloader(name)
        if loader == nil then
            error("unable to load module" .. name)
        end
 
        package.loaded[name] = true
        local res = loader(name)
        if res ~= nil then
            package.loaded[name] = res
        end
    end
    return package.loaded[name]
end

lua源码(在loadlib.c中):

1.首先是初始化require搜索路径流程

static const luaL_Reg pk_funcs[] = {
  {"loadlib", ll_loadlib},
  {"searchpath", ll_searchpath},
#if defined(LUA_COMPAT_MODULE)
  {"seeall", ll_seeall},
#endif
  /* placeholders */
  {"preload", NULL},
  {"cpath", NULL},
  {"path", NULL},
  {"searchers", NULL},
  {"loaded", NULL},
  {NULL, NULL}
};
 
 
static const luaL_Reg ll_funcs[] = {
#if defined(LUA_COMPAT_MODULE)
  {"module", ll_module},
#endif
  {"require", ll_require},
  {NULL, NULL}
};
 
 
static void createsearcherstable (lua_State *L) {
  static const lua_CFunction searchers[] =
    {searcher_preload, searcher_Lua, searcher_C, searcher_Croot, NULL};
  int i;
  /* create 'searchers' table */
  lua_createtable(L, sizeof(searchers)/sizeof(searchers[0]) - 1, 0);
  /* fill it with predefined searchers */
  for (i=0; searchers[i] != NULL; i++) {
    lua_pushvalue(L, -2);  /* set 'package' as upvalue for all searchers */
    lua_pushcclosure(L, searchers[i], 1);
    lua_rawseti(L, -2, i+1);
  }
#if defined(LUA_COMPAT_LOADERS)
  lua_pushvalue(L, -1);  /* make a copy of 'searchers' table */
  lua_setfield(L, -3, "loaders");  /* put it in field 'loaders' */
#endif
  lua_setfield(L, -2, "searchers");  /* put it in field 'searchers' */
}
 
 
/*
** create table CLIBS to keep track of loaded C libraries,
** setting a finalizer to close all libraries when closing state.
*/
static void createclibstable (lua_State *L) {
  lua_newtable(L);  /* create CLIBS table */
  lua_createtable(L, 0, 1);  /* create metatable for CLIBS */
  lua_pushcfunction(L, gctm);
  lua_setfield(L, -2, "__gc");  /* set finalizer for CLIBS table */
  lua_setmetatable(L, -2);
  lua_rawsetp(L, LUA_REGISTRYINDEX, &CLIBS);  /* set CLIBS table in registry */
}
 
 
LUAMOD_API int luaopen_package (lua_State *L) {
  createclibstable(L);
  luaL_newlib(L, pk_funcs);  /* create 'package' table */
  createsearcherstable(L);
  /* set paths */
  setpath(L, "path", LUA_PATH_VAR, LUA_PATH_DEFAULT);
  setpath(L, "cpath", LUA_CPATH_VAR, LUA_CPATH_DEFAULT);
  /* store config information */
  lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATH_SEP "\n" LUA_PATH_MARK "\n"
                     LUA_EXEC_DIR "\n" LUA_IGMARK "\n");
  lua_setfield(L, -2, "config");
  /* set field 'loaded' */
  luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
  lua_setfield(L, -2, "loaded");
  /* set field 'preload' */
  luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
  lua_setfield(L, -2, "preload");
  lua_pushglobaltable(L);
  lua_pushvalue(L, -2);  /* set 'package' as upvalue for next lib */
  luaL_setfuncs(L, ll_funcs, 1);  /* open lib into global table */
  lua_pop(L, 1);  /* pop global table */
  return 1;  /* return 'package' table */
}

2.require的源码

static int ll_require (lua_State *L) {
  const char *name = luaL_checkstring(L, 1);
  lua_settop(L, 1);  /* LOADED table will be at index 2 */
  lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
  lua_getfield(L, 2, name);  /* LOADED[name] */
  if (lua_toboolean(L, -1))  /* is it there? */
    return 1;  /* package is already loaded */
  /* else must load package */
  lua_pop(L, 1);  /* remove 'getfield' result */
  findloader(L, name);
  lua_pushstring(L, name);  /* pass name as argument to module loader */
  lua_insert(L, -2);  /* name is 1st argument (before search data) */
  lua_call(L, 2, 1);  /* run loader to load module */
  if (!lua_isnil(L, -1))  /* non-nil return? */
    lua_setfield(L, 2, name);  /* LOADED[name] = returned value */
  if (lua_getfield(L, 2, name) == LUA_TNIL) {   /* module set no value? */
    lua_pushboolean(L, 1);  /* use true as result */
    lua_pushvalue(L, -1);  /* extra copy to be returned */
    lua_setfield(L, 2, name);  /* LOADED[name] = true */
  }
  return 1;
}
 
static void findloader (lua_State *L, const char *name) {
  int i;
  luaL_Buffer msg;  /* to build error message */
  luaL_buffinit(L, &msg);
  /* push 'package.searchers' to index 3 in the stack */
  if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE)
    luaL_error(L, "'package.searchers' must be a table");
  /*  iterate over available searchers to find a loader */
  for (i = 1; ; i++) {
    if (lua_rawgeti(L, 3, i) == LUA_TNIL) {  /* no more searchers? */
      lua_pop(L, 1);  /* remove nil */
      luaL_pushresult(&msg);  /* create error message */
      luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1));
    }
    lua_pushstring(L, name);
    lua_call(L, 1, 2);  /* call it */
    if (lua_isfunction(L, -2))  /* did it find a loader? */
      return;  /* module loader found */
    else if (lua_isstring(L, -2)) {  /* searcher returned error message? */
      lua_pop(L, 1);  /* remove extra return */
      luaL_addvalue(&msg);  /* concatenate error message */
    }
    else
      lua_pop(L, 2);  /* remove both returns */
  }
}

根据源码知道,将所有的loader:

1.searcher_preload

搜索:package.preload 表中是否有指定lua

2.searcher_Lua

搜索:path 路径中是否有指定lua

3.searcher_C

搜索路径:cpath路径中是否有指定lua

4.searcher_Croot

搜索路径:cpath路径中是否有指定lua

都加入到了searchers表中,然后require(ll_requre)调用的时候,会先判断这个lua是否加载过,如果没有加载过那么就会调用

findloader,这个函数会遍历searchers表,依次在上面的4个函数中去找寻是否有对应的lua文件。

3.tolua中如何在lua中require其他lua

根据2我们知道require是遍历所有loader函数来所有lua文件,于是tolua中在searchers中加入了loader函数来进行搜索我们的lua文件,源码如下:

LuaState构造函数->ToLua.OpenLibs(L)->AddLuaLoader

static void AddLuaLoader(IntPtr L)
{
    LuaDLL.lua_getglobal(L, "package");
    LuaDLL.lua_getfield(L, -1, "loaders");
    LuaDLL.tolua_pushcfunction(L, Loader);

    for (int i = LuaDLL.lua_objlen(L, -2) + 1; i > 2; i--)
    {
        LuaDLL.lua_rawgeti(L, -2, i - 1);
        LuaDLL.lua_rawseti(L, -3, i);
    }

    LuaDLL.lua_rawseti(L, -2, 2);
    LuaDLL.lua_pop(L, 2);
}


[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
static int Loader(IntPtr L)
{
    try
    {
        string fileName = LuaDLL.lua_tostring(L, 1);
        fileName = fileName.Replace(".", "/");
        byte[] buffer = LuaFileUtils.Instance.ReadFile(fileName);

        if (buffer == null)
        {
            string error = LuaFileUtils.Instance.FindFileError(fileName);
            LuaDLL.lua_pushstring(L, error);
            return 1;
        }

        if (LuaConst.openLuaDebugger)
        {
            fileName = LuaFileUtils.Instance.FindFile(fileName);
        }                

        if (LuaDLL.luaL_loadbuffer(L, buffer, buffer.Length, "@"+ fileName) != 0)
        {
            string err = LuaDLL.lua_tostring(L, -1);
            throw new LuaException(err, LuaException.GetLastError());
        }

        return 1;
    }
    catch (Exception e)
    {
        return LuaDLL.toluaL_exception(L, e);
    }
}

可以看到这个loader函数最后会调用LuaFileUtils.Instance.ReadFile(fileName);来找寻要加载的文件,于是根据1中的介绍,我们知道也就是要在searchPaths中根据路径来找对应的lua文件。

你可能感兴趣的:(unity3D,lua)