找回密码
 立即注册
查看: 412|回复: 0

Unity toLua加载lua的流程

[复制链接]
发表于 2021-11-19 13:44 | 显示全部楼层 |阅读模式
转载注明出处: https://blog.csdn.net/qweewqpkn/article/details/86565686

1.Unity加载Lua文件的流程

Unity中我们要加载Lua文件调用的函数是:LuaState类中的DoFile(“xxx”).我们可以看到流程是:
LuaState:DoFile()->LuaState:LoadFileBuffer()->LuaFileUtils:ReadFile()->FindFile()
  1. publicvoidDoFile(string fileName){byte[] buffer =LoadFileBuffer(fileName);
  2.     fileName =LuaChunkName(fileName);LuaLoadBuffer(buffer, fileName);}
复制代码
  1. byte[]LoadFileBuffer(string fileName){#if UNITY_EDITORif(!beStart){thrownewLuaException("you must call Start() first to initialize LuaState");}#endifbyte[] buffer = LuaFileUtils.Instance.ReadFile(fileName);if(buffer ==null){string error =string.Format("cannot open {0}: No such file or directory", fileName);
  2.         error += LuaFileUtils.Instance.FindFileError(fileName);thrownewLuaException(error);}return buffer;}
复制代码
  1. publicvirtualbyte[]ReadFile(string fileName){if(!beZip){string path =FindFile(fileName);byte[] str =null;if(!string.IsNullOrEmpty(path)&& File.Exists(path)){#if !UNITY_WEBPLAYER
  2.             str = File.ReadAllBytes(path);#elsethrownewLuaException("can't run in web platform, please switch to other platform");#endif}return str;}else{returnReadZipFile(fileName);}}
复制代码
  1. publicstringFindFile(string fileName){if(fileName ==string.Empty){returnstring.Empty;}if(Path.IsPathRooted(fileName)){if(!fileName.EndsWith(".lua")){
  2.             fileName +=".lua";}return fileName;}if(fileName.EndsWith(".lua")){
  3.         fileName = fileName.Substring(0, fileName.Length -4);}string fullPath =null;for(int i =0; i < searchPaths.Count; i++){
  4.         fullPath = searchPaths[i].Replace("?", fileName);if(File.Exists(fullPath)){return fullPath;}}returnnull;}
复制代码
最后会在searchPaths的路径中进行搜索,是否存在指定的lua文件,来进行加载与否。
那这个searchPaths变量在哪里初始化呢?
第一处是AddSearchPath:
我们获取lua中的package.path来初始化我们的搜索路径。
  1. publicvoidInitPackagePath(){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('\\','/');
  2.             LuaFileUtils.Instance.AddSearchPath(path);}}LuaPushString("");LuaSetField(-3,"path");LuaPop(2);}
复制代码
第二处是LuaState类中的AddSearchPath:
InitLuaPath是在LuaState构造函数中调用的。
  1. voidInitLuaPath(){InitPackagePath();if(!LuaFileUtils.Instance.beZip){#if UNITY_EDITORif(!Directory.Exists(LuaConst.luaDir)){string msg =string.Format("luaDir path not exists: {0}, configer it in LuaConst.cs", LuaConst.luaDir);thrownewLuaException(msg);}if(!Directory.Exists(LuaConst.toluaDir)){string msg =string.Format("toluaDir path not exists: {0}, configer it in LuaConst.cs", LuaConst.toluaDir);thrownewLuaException(msg);}AddSearchPath(LuaConst.toluaDir);AddSearchPath(LuaConst.luaDir);#endifif(LuaFileUtils.Instance.GetType()==typeof(LuaFileUtils)){AddSearchPath(LuaConst.luaResDir);}}}
复制代码
2.lua中加载其他lua文件

lua中我们是使用require来进行加载其他lua文件的,首先require的伪代码实现如下:
  1. function require(name)if not package.loaded[name] then
  2.         local loader =findloader(name)if loader == nil then
  3.             error("unable to load module".. name)
  4.         end
  5.         package.loaded[name]= true
  6.         local res =loader(name)if res ~= nil then
  7.             package.loaded[name]= res
  8.         end
  9.     end
  10.     return package.loaded[name]
  11. end
复制代码
lua源码(在loadlib.c中):
1.首先是初始化require搜索路径流程
  1. staticconst 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}};staticconst luaL_Reg ll_funcs[]={#if defined(LUA_COMPAT_MODULE){"module", ll_module},#endif{"require", ll_require},{NULL,NULL}};staticvoid createsearcherstable (lua_State *L){staticconst 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' */#endiflua_setfield(L,-2,"searchers");/* put it in field 'searchers' */}/*
  2. ** create table CLIBS to keep track of loaded C libraries,
  3. ** setting a finalizer to close all libraries when closing state.
  4. */staticvoid 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 */}
  5. 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"
  6.                      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 */return1;/* return 'package' table */}
复制代码
2.require的源码
  1. staticint ll_require (lua_State *L){constchar*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? */return1;/* 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 */}return1;}staticvoid findloader (lua_State *L,constchar*name){int i;
  2.   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 */elseif(lua_isstring(L,-2)){/* searcher returned error message? */lua_pop(L,1);/* remove extra return */luaL_addvalue(&msg);/* concatenate error message */}elselua_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
  1. staticvoidAddLuaLoader(IntPtr L){
  2.     LuaDLL.lua_getglobal(L,"package");
  3.     LuaDLL.lua_getfield(L,-1,"loaders");
  4.     LuaDLL.tolua_pushcfunction(L, Loader);for(int i = LuaDLL.lua_objlen(L,-2)+1; i >2; i--){
  5.         LuaDLL.lua_rawgeti(L,-2, i -1);
  6.         LuaDLL.lua_rawseti(L,-3, i);}
  7.     LuaDLL.lua_rawseti(L,-2,2);
  8.     LuaDLL.lua_pop(L,2);}[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]staticintLoader(IntPtr L){try{string fileName = LuaDLL.lua_tostring(L,1);
  9.         fileName = fileName.Replace(".","/");byte[] buffer = LuaFileUtils.Instance.ReadFile(fileName);if(buffer ==null){string error = LuaFileUtils.Instance.FindFileError(fileName);
  10.             LuaDLL.lua_pushstring(L, error);return1;}if(LuaConst.openLuaDebugger){
  11.             fileName = LuaFileUtils.Instance.FindFile(fileName);}if(LuaDLL.luaL_loadbuffer(L, buffer, buffer.Length,"@"+ fileName)!=0){string err = LuaDLL.lua_tostring(L,-1);thrownewLuaException(err, LuaException.GetLastError());}return1;}catch(Exception e){return LuaDLL.toluaL_exception(L, e);}}
复制代码
可以看到这个loader函数最后会调用LuaFileUtils.Instance.ReadFile(fileName);来找寻要加载的文件,于是根据1中的介绍,我们知道也就是要在searchPaths中根据路径来找对应的lua文件。
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-25 12:31 , Processed in 0.129231 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表