2

在浏览了各种 C# 的 Lua 解释器之后,似乎只有一个是真正的纯 c# - MoonSharp。后来成为 NLua 的 LuaInterpreter(从 2009 年起已停用)依赖于其他两个 c# 库之一 KeraLua 或另一个库,并且需要自定义的 lua52.dll(您不能使用来自 lua.org 的那个)。他们有一个已关闭的错误报告,上面写着查看自述文件以获取他们定制的 lua52.dll 的下载位置,但它不存在。您被迫从各种来源下载这些库,并祈祷它们一起工作,此外还有一个多文件分发,由于最终用户计算机上的几个 lua52.dll 变体(假设他们将使用不仅仅是你的程序)。

NLua 上一个闪亮的灯塔是它显然很受欢迎,但是该项目几年来没有收到任何重大更新。另一方面,MoonSharp 似乎是完全独立的,但缺乏常见任务的文档,例如加载使用 lua 构建的表并使用它。

我根据他们在 Git 上提供的单个示例提出了以下代码,然后在他们的网站上复制了 moonsharp.org(以先到者为准,我不确定,但只有 1 个示例还不够):

using System;
using System.IO;
using MoonSharp.Interpreter;

class Foo {

       function Bar( string accountName, string warcraftPath ) {
             string datastore = Path.Combine(warcraftPath, "WTF", "Account", accountName, "SavedVariables", "DataStore_Containers.lua";

             DynValue table = Script.RunString( File.ReadAllText( datastore ) );

             Console.WriteLine( table.Table.Keys.Count().ToString() );
       }
}

结果如下(图片中的代码略有不同,因为我在此处调整了粘贴的代码以保持清洁,并使您更容易使用下面 pastebin 链接中的表格数据重现问题。)

MoonSharp.Interpreter 表加载 Lua 失败

我正在尝试阅读的表格如下所示(由于大小超过 30,000 个字符,因此必须在 pastebin 上进行简化):

魔兽世界 - Datastore_Containers Lua 表示例数据

我有某种工作,它有点hackish,但似乎并没有离开遍历值或显式获取子表/值或值的键。

    Script s = new Script(CoreModules.Preset_Complete);

    // hacked by appending ' return DataStore_ContainersDB ' to return the table as DoString seems to only work to run a function expecting a result to be returned.
    DynValue dv = s.DoString(luaTable + "\nreturn DataStore_ContainersDB;");
    Table t = dv.Table;
    foreach(var v in t.Keys)
    {
        Console.WriteLine( v.ToPrintString() );
    }

问题是我似乎没有任何方法可以输入子表结果集或显式访问类似t["global"]or的结果集t.global

4

2 回答 2

1

设法破解和削减我的方式并提出一个可行的解决方案,尽管它相当初级(可能有人可以采用这个概念并使对子数据的访问更加合理:

Script s = new Script(CoreModules.Preset_Complete);
DynValue dv = s.DoString(luaTable + "\nreturn DataStore_ContainersDB;");
Table t =  dv.Table;
Table global;
global = t.Get("global").ToObject<Table>().Get("Characters").ToObject<Table>();

foreach (var key in global.Keys)
{
    Console.WriteLine( key.ToString() );
}

MoonSharp 库似乎需要并严重依赖于Script类,这是所有其他方法操作的前提。该DoString方法需要返回结果,否则DynValue将始终为 void/null 。 DynValue似乎是整个 Lua 进程的基本全局处理程序,它可以处理方法(又名,该 lua 字符串可能包含 DynValue 将公开的几个方法,并允许在 C# 中调用它们,将响应作为其他 DynValue 的返回)

因此,如果您希望加载一个仅包含 Lua 表格格式的日期的 lua 文件,您必须在最后一行附加一个带有表格名称的返回值。这就是为什么你看到:

"\nreturn DataStore_ContainersDB;"

...因为表名称为“DataStore_ContainersDB”

接下来,必须将结果加载到一个新Table对象中,因为 DynValue 不是一个实际的表,而是一个类结构,用于保存所有可用的各种格式(方法、表等)。

在它变成表格格式后,您现在可以通过键名、数字或 DynValue 调用键/值对来使用它。就我而言,因为我知道原始键名,所以我直接调用存在我不知道并希望使用的键名的表。

Table.Get(键)

由于这返回一个 DynValue,因此我们必须再次将对象转换/加载为表,这使用该.ToObject<>方法很方便。

我提供的foreach循环然后循环遍历位于子表中的可用键:全局>字符> *

...然后我将密钥名称写到控制台使用key.ToString()

如果还有其他子表,在这个例子中(因为有),你可以通过在foreach循环中使用相同的概念来遍历未知的子表,如下所示:

foreach (var key in global.Keys)
{
    if(IsTable(global.Get(key.String)))
    {
        Console.WriteLine("-------" + key.ToPrintString() + "-------");
        Table characterData = global.Get(key.String).ToObject<Table>();
        foreach (var characterDataField in characterData.Keys)
        {
            if( !IsTable(characterData.Get(characterDataField.String)))
            {
                Console.WriteLine(string.Format("{0} = {1}", characterDataField.ToPrintString(), characterData.Get(characterDataField.String).ToPrintString()));
            }
            else
            {
                Console.WriteLine(string.Format("{0} = {1}", characterDataField.ToPrintString(), "Table[]"));
            }
        }
        Console.WriteLine("");
    }
}

...这是我写的快速检查数据是否为表格的方法。这是IsTable()上面foreach示例中使用的方法。

private static bool IsTable(DynValue table)
{
    switch (table.Type)
    {
        case DataType.Table:
            return true;

        case DataType.Boolean:
        case DataType.ClrFunction:
        case DataType.Function:
        case DataType.Nil:
        case DataType.Number:
        case DataType.String:
        case DataType.TailCallRequest:
        case DataType.Thread:
        case DataType.Tuple:
        case DataType.UserData:
        case DataType.Void:
        case DataType.YieldRequest:
            break;
    }
    return false;
}

我已经尽我所能使这个可行,但是,如前所述,我确实看到了改进递归的空间。检查每个子对象的数据类型,然后加载它感觉非常多余,似乎可以简化。

我对这个问题的其他解决方案持开放态度,理想情况下是一些增强的形式,这会使它使用起来不那么笨重。

于 2016-07-10T09:43:34.373 回答
0

用于处理表中的表,这是我首选的做事方式。我想出了这个。

Script s = new Script();
s.DoString(luaCode);
Table tableData = s.Globals[rootTableIndex] as Table;

for (int i = 1; i < tableData.Length + 1; i++) {
    Table subTable = tableData.Get(i).Table;

    //Do cool stuff here with the data
}

当然,这需要您知道全局根表的索引。

为了我的使用,我做了以下事情(仍在测试)

    string luaCode = File.ReadAllText(Path.Combine(weaponDataPath, "rifles.Lua"));

    Script script = new Script();        
    script.DoString(luaCode);
    Gun rifle = new Gun();
    Table rifleData = script.Globals["rifles"] as Table;

    for (int i = 1; i < rifleData.Length + 1; i++) {

        Table rifleTable = rifleData.Get(i).Table;

        rifle.Name = rifleTable.Get("Name").String;
        rifle.BaseDamage = (int)rifleTable.Get("BaseDamage").Number;
        rifle.RoundsPerMinute = (int)rifleTable.Get("RoundsPerMinute").Number;
        rifle.MaxAmmoCapacity = (int)rifleTable.Get("MaxAmmoCapacity").Number;
        rifle.Caliber = rifleTable.Get("Caliber").String;
        rifle.WeaponType = "RIFLE";

        RiflePrototypes.Add(rifle.Name, rifle);
    }  

这需要对表以及值的命名方式进行一些假设,但是如果您将其用于对象成员分配,我不明白为什么您会关心表中不属于您定义的对象的元素assignment type.Member = table.Get(成员等效索引).member type

于 2017-07-18T06:20:38.730 回答