如何用C#实现Lua容器(虚拟文件系统)模块加载器
听起来有点可怕不是吗?
一些背景信息,我想使用 LuaInterface 将包含一些 lua 模块的 tar 存档加载到我的 C# 应用程序中。最简单的方法是将这些文件提取到临时文件夹,修改 lua 模块搜索路径并像往常一样使用 require 读取它们。但我不想将这些脚本放在文件系统上的某个位置。
所以我认为应该可以使用 #ziplib 加载 tar 存档 我知道有很多 lua tar 之类的实现。但#zlib 已经是该项目的一部分。
成功地将文件作为字符串(流)从存档中加载出来后,我应该能够通过 LuaInterface 将它们传递到 C# 中的 lua.DoString(...) 中。
但是,如果模块有这样一行:“module(..., package.seeall)”,则仅通过 dostring 或 dofile 加载模块是行不通的。有一个错误报告,例如将参数 1 传递为 nil,但需要字符串。
另一个问题是一个模块可能依赖于也位于 tar 存档中的其他模块。
一种可能的解决方案应该是定义一个自定义加载器,如此处所述。
我的想法是使用 #ziplib 在 C# 中实现这样的加载器,并将该加载器映射到我的 C# 应用程序的 lua 堆栈中。
你们中有人有类似的任务吗? 是否有任何现成的解决方案可以解决此类问题?
tar 文件不是必须有的,但是是一种很好的包格式。
这个想法可行还是完全不可行?
我编写了一些示例类来从存档中提取 lua 文件。该方法作为加载器并返回一个 lua 函数。
namespace LuaInterfaceTest
{
class LuaTarModuleLoader
{
private LuaTarModuleLoader() { }
~LuaTarModuleLoader()
{
in_stream_.Close();
}
public LuaTarModuleLoader(Stream in_stream,Lua lua )
{
in_stream_ = in_stream;
lua_ = lua;
}
public LuaFunction load(string modulename, out string error_message)
{
string lua_chunk = "test=hello";
string filename = modulename + ".lua";
error_message = "Unable to locate the file";
in_stream_.Position = 0; // rewind
Stream gzipStream = new BZip2InputStream(in_stream_);
TarInputStream tar = new TarInputStream(gzipStream);
TarEntry tarEntry;
LuaFunction func = null;
while ((tarEntry = tar.GetNextEntry()) != null)
{
if (tarEntry.IsDirectory)
{
continue;
}
if (filename == tarEntry.Name)
{
MemoryStream out_stream = new MemoryStream();
tar.CopyEntryContents(out_stream);
out_stream.Position = 0; // rewind
StreamReader stream_reader = new StreamReader(out_stream);
lua_chunk = stream_reader.ReadToEnd();
func = lua_.LoadString(lua_chunk, filename);
string dum = func.ToString();
error_message = "No Error!";
break;
}
}
return func;
}
private Stream in_stream_;
private Lua lua_;
}
我尝试
在 LuaInterface 中注册这样的加载方法
Lua lua = new Lua();
GC.Collect();
Stream inStream = File.OpenRead("c:\\tmp\\lua_scripts.tar.bz2");
LuaTarModuleLoader tar_loader = new LuaTarModuleLoader(inStream, lua);
lua.DoString("require 'CLRPackage'");
lua.DoString("import \"ICSharpCode.SharpZipLib.dll\"");
lua.DoString("import \"System\"");
lua["container_module_loader"] = tar_loader;
lua.DoString("table.insert(package.loaders, 2, container_module_loader.load)");
lua.DoString("require 'def_sensor'");
如果我尝试这种方式,我会在调用 require 时得到异常:
“实例方法'load'需要一个非空目标对象”
我尝试直接调用load方法,这里我必须使用“:”符号。
lua.DoString("container_module_loader:load('def_sensor')");
如果我调用这样的方法,我会在调试器中命中一个断点,该断点位于该方法的顶部,因此一切都会按预期工作。
但是,如果我尝试使用“:”符号注册该方法,则会在注册该方法时遇到异常:
lua.DoString("table.insert(package.loaders, 2, container_module_loader:load)");
“[字符串“chunk”]:1:函数参数应位于 ')' 附近”
Sounds a little bit scary isn't it?
Some background information, I want to load a tar archive which contains some lua modules into my C# application using LuaInterface. The easiest way would be to extract these files to a temp folder, modify the lua module search path and read them with require as usual. But I do not want to put these scripts somewhere on the file system.
So I thought it should be possible to load the tar-archive with the #ziplib I know there are a lot of lua implementations for tar and stuff like that. But the #zlib is already part of the project.
After successfully loading the file as strings(streams) out of the archive I should be able to pass them into lua.DoString(...) in C# via LuaInterface.
But simply loading modules by a dostring or dofile does not work if modules have a line like this: "module(..., package.seeall)" There is a error reportet like passing argument 1 a nil, but string expected.
The other problem is a module may depend on other modules which are also located in the tar archive.
One possible solution should be to define a custom loader as described here.
My idea is to implement such a loader in C# with the #ziplib and map this loader into the lua stack of my C# application.
Does anyone of you had a similar task to this?
Are there any ready to use solutions which already address problems like this?
The tar file is not must have but a nice to have package format.
Is this idea feasible or totally unfeasible?
I've written some example class to extract the lua files from the archive. This method works as loader and return a lua function.
namespace LuaInterfaceTest
{
class LuaTarModuleLoader
{
private LuaTarModuleLoader() { }
~LuaTarModuleLoader()
{
in_stream_.Close();
}
public LuaTarModuleLoader(Stream in_stream,Lua lua )
{
in_stream_ = in_stream;
lua_ = lua;
}
public LuaFunction load(string modulename, out string error_message)
{
string lua_chunk = "test=hello";
string filename = modulename + ".lua";
error_message = "Unable to locate the file";
in_stream_.Position = 0; // rewind
Stream gzipStream = new BZip2InputStream(in_stream_);
TarInputStream tar = new TarInputStream(gzipStream);
TarEntry tarEntry;
LuaFunction func = null;
while ((tarEntry = tar.GetNextEntry()) != null)
{
if (tarEntry.IsDirectory)
{
continue;
}
if (filename == tarEntry.Name)
{
MemoryStream out_stream = new MemoryStream();
tar.CopyEntryContents(out_stream);
out_stream.Position = 0; // rewind
StreamReader stream_reader = new StreamReader(out_stream);
lua_chunk = stream_reader.ReadToEnd();
func = lua_.LoadString(lua_chunk, filename);
string dum = func.ToString();
error_message = "No Error!";
break;
}
}
return func;
}
private Stream in_stream_;
private Lua lua_;
}
}
I try to register the load method like this in the LuaInterface
Lua lua = new Lua();
GC.Collect();
Stream inStream = File.OpenRead("c:\\tmp\\lua_scripts.tar.bz2");
LuaTarModuleLoader tar_loader = new LuaTarModuleLoader(inStream, lua);
lua.DoString("require 'CLRPackage'");
lua.DoString("import \"ICSharpCode.SharpZipLib.dll\"");
lua.DoString("import \"System\"");
lua["container_module_loader"] = tar_loader;
lua.DoString("table.insert(package.loaders, 2, container_module_loader.load)");
lua.DoString("require 'def_sensor'");
If I try it this way I'll get an exception while the call to require :
"instance method 'load' requires a non null target object"
I tried to call the load method directly, here I have to use the ":" notation.
lua.DoString("container_module_loader:load('def_sensor')");
If I call the method like that I hit a breakpoint in the debugger which is place on top of the method so everything works as expected.
But If I try to register the method with ":" notation I get an exception while registering the method:
lua.DoString("table.insert(package.loaders, 2, container_module_loader:load)");
"[string "chunk"]:1: function arguments expected near ')'"
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在 LÖVE 中,他们做到了这一点。所有 Lua 文件都位于一个 zip 文件内,即使使用了
...
,它们也能正常工作。他们使用的库是PhysicsFS。查看来源。也许 /modules/filesystem 会让你开始。
In LÖVE they have that working. All Lua files are inside one zip file, and they work, even if
...
is used. The library they use is PhysicsFS.Have a look at the source. Probably /modules/filesystem will get you started.
我终于掌握了窍门;-)
我目前不太明白的一个问题是我的加载程序不应返回任何字符串。
这是我的解决方案:
加载器类本身:
}
以及如何使用加载器,我不确定是否真的需要所有包的东西。但我必须用“:”符号结束调用并将其隐藏在“load_wrapper”函数后面。
我希望这也可以帮助其他人
I finally got the trick ;-)
One Problem I currently not really understand is that my loader should not return any string.
Here is my solution:
The loader Class itself:
}
And how to use the loader, I am not sure if all the package stuff is really needed. But I had to wrap up the call with ":" notation and hide it behind my "load_wrapper" function.
I hope this may also helps some other