如何使用MoonSharp加载lua表?

时间:2016-07-10 05:02:08

标签: c# lua moonsharp

通过各种Lua解释器查看C#之后,似乎只有一个是纯粹的c# - MoonSharp。后来成为NLua的LuaInterpreter(从2009年解散)依赖于另外两个c#librariers KeraLua或另一个lib,并且需要一个自定义的lua52.dll(你不能使用lua.org中的那个,和)。他们有一个错误报告,该报告已关闭,请查看自定义lua52.dll下载位置的自述文件,但它不存在。您被迫从各种来源下载这些库,并祈祷它们一起工作,另外还有一个多文件分发,由于最终用户计算机上的几个lua52.dll变体,可能会导致与其他程序的兼容性问题(假设他们将使用不仅仅是你的计划。)

在NLua上闪亮的光明灯塔显然很受欢迎,但该项目在几年内没有收到任何重大更新。另一方面,MoonSharp看起来完全是自包含的,但缺少常见任务的文档,例如加载用lua构建的表并使用它。

我基于他们在Git上提供的单个示例提出了以下代码,然后在他们的网站moonsharp.org上复制(以先到者为准,我不确定,但有一个例子是不够的):< / p>

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 Table Load Lua Failure

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

World of Warcraft - Datastore_Containers Lua table sample data

我有点工作,有点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"]t.global之类的结果集。

2 个答案:

答案 0 :(得分: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&#返回响应39; S)

因此,如果您希望加载仅包含Lua表格格式的日期的lua文件,则必须附加一个表格作为最后一行的返回。这就是你看到的原因:

"\nreturn DataStore_ContainersDB;"

...因为表名被称为&#34; DataStore_ContainersDB&#34;

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

在表格格式之后,您现在可以通过键名称,数字或DynValue调用键/值对来使用它。在我的情况下,因为我知道原始的密钥名称,所以我直接调用表格,其中存在我不知道并且想要使用的密钥名称。

  

Table.Get(Key)

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

我提供的foreach循环遍历子表中可用的键,位于:global&gt;字符&gt; *

...然后我使用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;
}

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

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

答案 1 :(得分: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
}

当然,这要求您知道Global rootTable的索引。

对于我的使用,我执行以下操作(仍在测试)

    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);
    }  

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