修改lua的文件加载器,自定义lua文件加载

2018-08-03 16:23:15 浏览数 (1)

Lua提供高级的require函数来加载运行库。

1. require会搜索目录加载文件

2. require会判断是否文件已经加载避免重复加载同一文件。

require使用的路径和普通我们看到的路径还有些区别,我们一般见到的路径都是一个目录列表。require的路径是一个模式列表,每一个模式指明一种由虚文件名(require的参数)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号的文件名。匹配的时候Lua会首先将问号用虚文件名替换,然后看是否有这样的文件存在。如果不存在继续用同样的方法用第二个模式匹配。例如,路径如下:

代码语言:javascript复制
?;?.lua;c:windows?;/usr/local/lua/?/?.lua

调用require "lili"时会试着打开这些文件:

代码语言:javascript复制
lili
lili.lua
c:windowslili
/usr/local/lua/lili/lili.lua

require关注的问题只有分号(模式之间的分隔符)和问号,其他的信息(目录分隔符,文件扩展名)在路径中定义。

为了确定路径,Lua首先检查全局变量LUA_PATH是否为一个字符串,如果是则认为这个串就是路径;否则require检查环境变量LUA_PATH的值,如果两个都失败require使用固定的路径(典型的"?;?.lua")

我们在lua中加载文件只能配置LUA_PATH的值。但是我们在进行游戏开发的时候,脚本的路径可能是千变万化的,而且有可能是需要打包到一个专有的文件格式里面,这时候原生lua的加载就会出现很多问题了,有没有更好的方案来加载lua文件呢?接下来我介绍一种方案来解决这种问题。

自定义lua文件加载器

我们可以自定义一个lua文件的加载器,去替换原生lua的加载器,怎讲加载lua文件由我们自己决定。

首先我们应该有一个文件系统来加载资源文件,如果没有也没关系,可以自己写一个加载文件的接口。然后我们写一段代码来调用文件系统或者我们自己写的接口来加载文件到内存。

示例代码如下:

代码语言:javascript复制
/** 载入lua文件
@param L lua状态机  
@param name 需要加载的文件名 
@return  加载成功返回0
*/
static int luaA_LoadFile(lua_State *L, const char *name)
{
	int status = -1;
	char filename[256];
	sprintf(filename, "%s.lua", name);
	FileSystem * pFileSystem = getFileSystem();
	if(pFileSystem != NULL)
	{
		Stream * pStream = pFileSystem->open(filename);
		if(pStream != NULL)
		{
			Uint32 nLength = pStream->getLength();
			if(nLength > 0)
			{
				// 把文件读进内存
				char * pData = new char[nLength   1]; pData[nLength] = 0;
				pStream->read(pData, nLength);
				//pStream->close();
				// 通过内存加载文件
				status = luaL_loadbuffer(L, pData, nLength, pData);
				delete[] pData;
			}
			pStream->release();
		}
	}
	return status;
}

上面的加载文件的代码可以自己实现。

下一步我们就写一个函数上面的加载函数设置给lua,替换lua的原生加载器。

代码语言:javascript复制

static int luaA_SetLoader(lua_State *L, lua_CFunction fn)
{
	lua_getglobal(L, LUA_LOADLIBNAME);
	if (lua_istable(L, -1)) 
	{
		lua_getfield(L, -1, "loaders");
		if (lua_istable(L, -1))
 		{
			lua_pushcfunction(L, fn);
			lua_rawseti(L, -2, 2);
			return 0;
		}
	}
	return -1;
}

做好上面两步基本上就已经完成了,有时候我们还用dofile来加载文件,所以我们需要为dofile也写一个类似的函数。

代码语言:javascript复制
static int luaA_DoFile(lua_State * L)
{
	size_t l;
	const char* sFileName = luaL_checklstring(L, 1, &l);
	if(sFileName != NULL )
	{
		luaA_LoadFile(L, sFileName);
		return 1;
	}
	return 0;
}

最后,我们在初始化luaState的时候进行设置就行啦!

代码语言:javascript复制
bool CLuaEngine::Create(bool bFromPackLoadLua)
{
	// 初始化LUA库
	m_pLuaState = lua_open();
	if(m_pLuaState == NULL)
	{
		return false;
	}
	// 初始化所有的标准库
	luaL_openlibs(m_pLuaState);
	// 替换缺省的Lua加载函数
	luaA_SetLoader(m_pLuaState, luaA_LoadFile);
	// 初始化一些基本的api
	lua_register(m_pLuaState, "dofile", luaA_DoFile);
	
	return true;
}

好了,这样无论lua文件放在那里,我们就可以随心所欲的加载lua文件啦,就像加载其他文件一样了。

0 人点赞