LuaFormula(Fungus版)もつくってみた


FungusにはLuaというかMoonsharpが組み込まれているのでFungus版のLuaformulaも作ってみた

どうやらsayなんかのFungus組み込みのLua関数はコルーチン経由で呼ばないとエラーで動かないらしい。

```C#
/// <summary>
/// Fungus Luaコードの実行(終了待機版)
/// </summary>
[UnitCategory("Fungus")]
[UnitTitle("Lua Formula(Wait)")]
public class FungusLuaFormulaWait : Unit
{
    [DoNotSerialize]
    [PortLabelHidden]
    public ControlInput InputFlow { get; private set; }

    [DoNotSerialize]
    [PortLabelHidden]
    public ControlOutput OutputFlow { get; private set; }

    [Serialize]
    [Inspectable(order = int.MaxValue)]
    [InspectorExpandTooltip]
    public bool cacheArguments { get; set; }

    [DoNotSerialize]
    [Inspectable, UnitHeaderInspectable("Inputs")]
    public virtual int inputCount
    {
        get
        {
            return _inputCount;
        }
        set
        {
            _inputCount = Mathf.Clamp(value, minInputCount, maxInputCount);
        }
    }

    [DoNotSerialize]
    public ReadOnlyCollection<ValueInput> multiInputs { get; protected set; }

    [SerializeAs(nameof(inputCount))]
    private int _inputCount = 0;

    [DoNotSerialize]
    protected virtual int maxInputCount => 9;

    [DoNotSerialize]
    protected virtual int minInputCount => 0;

    [Inspectable, InspectorTextArea, UnitHeaderInspectable]
    public string LuaCode { get; private set; }

    [DoNotSerialize]
    [NullMeansSelf]
    public ValueInput GameObject { get; private set; }

    [DoNotSerialize]
    public ValueInput FlowChart { get; private set; }

    [DoNotSerialize]
    public ValueInput EventName { get; private set; }

    [DoNotSerialize]
    public ValueOutput Result { get; private set; }

    [DoNotSerialize]
    private object result;

    protected override void Definition()
    {
        InputFlow = ControlInputCoroutine(nameof(InputFlow), ExecuteWait);
        OutputFlow = ControlOutput(nameof(OutputFlow));

        GameObject = ValueInput<GameObject>(nameof(GameObject), null).NullMeansSelf();
        FlowChart = ValueInput<Flowchart>(nameof(FlowChart), null);
        EventName = ValueInput<string>(nameof(EventName), string.Empty);

        var mi = new List<ValueInput>();
        multiInputs = mi.AsReadOnly();

        for (var i = 0; i < inputCount; i++)
        {
            mi.Add(ValueInput<object>(((char)('a' + i)).ToString()));
        }

        foreach (var input in multiInputs)
        {
            input.AllowsNull();
        }

        Result = ValueOutput(nameof(Result), _ => result);


        Requirement(FlowChart, InputFlow);
        Requirement(GameObject, InputFlow);

        Succession(InputFlow, OutputFlow);
    }

    public IEnumerator ExecuteWait(Flow flow)
    {
        bool complete = false;
        Flowchart flowchart = flow.GetValue<Flowchart>(FlowChart);
        GameObject gameobject = flow.GetValue<GameObject>(GameObject);
        string eventname = flow.GetValue<string>(EventName);
        LuaEnvironment luaenvironment = null;
        if (flowchart != null) luaenvironment = flowchart.LuaEnv;
        if (luaenvironment == null) luaenvironment = LuaEnvironment.GetLua();
        if (luaenvironment != null)
        {
            for (var i = 0; i < inputCount; i++)
            {
                if (multiInputs[i].hasValidConnection)
                {
                    var obj = flow.GetValue<object>(multiInputs[i]);
                    luaenvironment.Interpreter.Globals[((char)('A' + i)).ToString()] = obj;
                }
            }

            Closure luaFunction = luaenvironment.LoadLuaFunction(LuaCode, "chunk");
            luaenvironment.RunLuaFunction(luaFunction, true, (ResultValue) =>
            {
                if (eventname.CompareTo(string.Empty) != 0)
                {
                    result = LuaVariable.GetValue(ResultValue);
                    CustomEvent.Trigger(gameobject, eventname, result);
                }

                if (!cacheArguments)
                {
                    for (var i = 0; i < maxInputCount; i++)
                    {
                        luaenvironment.Interpreter.Globals[(((char)('A' + i)).ToString())] = null;
                    }
                }

                complete = true;
            });

            yield return new WaitUntil(() => complete);
        }

        yield return OutputFlow;
    }
}

/// <summary>
/// Fungus Luaコードの実行(非同期版)
/// </summary>
[UnitCategory("Fungus")]
[UnitTitle("Lua Formula(No Wait)")]
public class FungusLuaFormulaNoWait : Unit
{
    [DoNotSerialize]
    [PortLabelHidden]
    public ControlInput InputFlow { get; private set; }

    [DoNotSerialize]
    [PortLabelHidden]
    public ControlOutput OutputFlow { get; private set; }

    [Serialize]
    [Inspectable(order = int.MaxValue)]
    [InspectorExpandTooltip]
    public bool cacheArguments { get; set; }

    [DoNotSerialize]
    [Inspectable, UnitHeaderInspectable("Inputs")]
    public virtual int inputCount
    {
        get
        {
            return _inputCount;
        }
        set
        {
            _inputCount = Mathf.Clamp(value, minInputCount, maxInputCount);
        }
    }

    [DoNotSerialize]
    public ReadOnlyCollection<ValueInput> multiInputs { get; protected set; }

    [SerializeAs(nameof(inputCount))]
    private int _inputCount = 0;

    [DoNotSerialize]
    protected virtual int maxInputCount => 9;

    [DoNotSerialize]
    protected virtual int minInputCount => 0;

    [Inspectable, InspectorTextArea, UnitHeaderInspectable]
    public string LuaCode { get; private set; }

    [DoNotSerialize]
    [NullMeansSelf]
    public ValueInput GameObject { get; private set; }

    [DoNotSerialize]
    public ValueInput FlowChart { get; private set; }

    [DoNotSerialize]
    public ValueInput EventName { get; private set; }

    protected override void Definition()
    {
        InputFlow = ControlInput(nameof(InputFlow), ExecuteWait);
        OutputFlow = ControlOutput(nameof(OutputFlow));

        GameObject = ValueInput<GameObject>(nameof(GameObject), null).NullMeansSelf();
        FlowChart = ValueInput<Flowchart>(nameof(FlowChart), null);
        EventName = ValueInput<string>(nameof(EventName), string.Empty);

        var mi = new List<ValueInput>();
        multiInputs = mi.AsReadOnly();

        for (var i = 0; i < inputCount; i++)
        {
            mi.Add(ValueInput<object>(((char)('a' + i)).ToString()));
        }

        foreach (var input in multiInputs)
        {
            input.AllowsNull();
        }

        Requirement(FlowChart, InputFlow);
        Requirement(GameObject, InputFlow);

        Succession(InputFlow, OutputFlow);
    }

    public ControlOutput ExecuteWait(Flow flow)
    {
        //bool _complete = false;
        Flowchart flowchart = flow.GetValue<Flowchart>(FlowChart);
        GameObject gameobject = flow.GetValue<GameObject>(GameObject);
        string eventname = flow.GetValue<string>(EventName);
        LuaEnvironment luaenvironment = null;
        if (flowchart != null) luaenvironment = flowchart.LuaEnv;
        if (luaenvironment == null) luaenvironment = LuaEnvironment.GetLua();
        if (luaenvironment != null)
        {
            for (var i = 0; i < inputCount; i++)
            {
                if (multiInputs[i].hasValidConnection)
                {
                    var obj = flow.GetValue<object>(multiInputs[i]);
                    luaenvironment.Interpreter.Globals[((char)('A' + i)).ToString()] = obj;
                }
            }

            Closure luaFunction = luaenvironment.LoadLuaFunction(LuaCode, "chunk");
            luaenvironment.RunLuaFunction(luaFunction, true, (ResultValue) =>
            {
                if (eventname.CompareTo(string.Empty) != 0)
                {
                    object result = LuaVariable.GetValue(ResultValue);
                    CustomEvent.Trigger(gameobject, eventname, result);
                }

                if (!cacheArguments)
                {
                    for (var i = 0; i < maxInputCount; i++)
                    {
                        luaenvironment.Interpreter.Globals[(((char)('A' + i)).ToString())] = null;
                    }
                }
            });
        }

        return OutputFlow;
    }
}

/// <summary>
/// Fungus Lua グローバル変数の設定
/// </summary>
[UnitCategory("Fungus")]
[UnitTitle("Set Lua Global Variable")]
public class FungusSetVariable : Unit
{
    [DoNotSerialize]
    [PortLabelHidden]
    public ControlInput InputFlow { get; private set; }

    [DoNotSerialize]
    [PortLabelHidden]
    public ControlOutput OutputFlow { get; private set; }

    [DoNotSerialize]
    public ValueInput FlowChart { get; private set; }

    [DoNotSerialize]
    public ValueInput VariableName { get; private set; }

    [DoNotSerialize]
    public ValueInput VariableData { get; private set; }

    protected override void Definition()
    {
        InputFlow = ControlInput(nameof(InputFlow), SetVariable);
        OutputFlow = ControlOutput(nameof(OutputFlow));

        FlowChart = ValueInput<Flowchart>(nameof(FlowChart), null);
        VariableName = ValueInput<string>(nameof(VariableName), string.Empty);
        VariableData = ValueInput<object>(nameof(VariableData), null);

        Requirement(FlowChart, InputFlow);
        Requirement(VariableName, InputFlow);
        Requirement(VariableData, InputFlow);

        Succession(InputFlow, OutputFlow);
    }

    public ControlOutput SetVariable(Flow flow)
    {
        Flowchart flowchart = flow.GetValue<Flowchart>(FlowChart);
        string valname = flow.GetValue<string>(VariableName);
        object valdata = flow.GetValue<string>(VariableData);
        LuaEnvironment luaenvironment = null;

        if (flowchart != null) luaenvironment = flowchart.LuaEnv;
        if (luaenvironment == null) luaenvironment = LuaEnvironment.GetLua();
        if (luaenvironment != null)
        {
            luaenvironment.Interpreter.Globals[valname] = valdata;
        }

        return OutputFlow;
    }
}

/// <summary>
/// Fungus Lua グローバル変数の取得
/// </summary>
[UnitCategory("Fungus")]
[UnitTitle("Get Lua Global Variable")]
public class FungusGetVariable : Unit
{
    [DoNotSerialize]
    public ValueInput FlowChart { get; private set; }

    [DoNotSerialize]
    public ValueInput VariableName { get; private set; }

    [DoNotSerialize]
    public ValueOutput Result { get; private set; }

    protected override void Definition()
    {
        FlowChart = ValueInput<Flowchart>(nameof(FlowChart), null);
        VariableName = ValueInput<string>(nameof(VariableName), string.Empty);

        Result = ValueOutput(nameof(Result), GetVariable);

        Requirement(FlowChart, Result);
        Requirement(VariableName, Result);
    }

    public object GetVariable(Flow flow)
    {
        Flowchart flowchart = flow.GetValue<Flowchart>(FlowChart);
        string valname = flow.GetValue<string>(VariableName);
        object result = null;
        LuaEnvironment luaenvironment = null;

        if (flowchart != null) luaenvironment = flowchart.LuaEnv;
        if (luaenvironment == null) luaenvironment = LuaEnvironment.GetLua();
        if (luaenvironment != null)
        {
            result = LuaVariable.GetValue(luaenvironment.Interpreter.Globals.Get(valname));
        }

        return result;
    }
}

/// <summary>
/// DynVal変換クラス
/// </summary>
public static class LuaVariable
{
    public static LuaVariableType GetType(DynValue val)
    {
        if (val == null) return LuaVariableType.TYPE_NULL;

        DataType valType = val.Type;

        switch (valType)
        {
            case DataType.Boolean:
                return LuaVariableType.TYPE_BOOLEAN;

            case DataType.Number:
                if (val.Number % 1 == 0)
                {
                    return LuaVariableType.TYPE_LONG;
                }
                else
                {
                    return LuaVariableType.TYPE_DOUBLE;
                }

            case DataType.String:
                return LuaVariableType.TYPE_STRING;

            case DataType.Tuple:
                return LuaVariableType.TYPE_LIST;

            case DataType.Table:
                bool isDic = false;

                foreach (var key in val.Table.Keys)
                {
                    if (key.Type == DataType.String)
                    {
                        isDic = true;
                    }
                }
                return isDic ? LuaVariableType.TYPE_DICTIONARY : LuaVariableType.TYPE_LIST;

            case DataType.UserData:
                if (val.UserData.Object != null) return LuaVariableType.TYPE_CLROBJECT; 
                if (val.UserData.Descriptor.Type != null) return LuaVariableType.TYPE_CLRTYPE;
                return LuaVariableType.TYPE_NULL;

            case DataType.Function:
                return LuaVariableType.TYPE_FUNCTION;

            case DataType.ClrFunction:
                return LuaVariableType.TYPE_CALLBACK;

            case DataType.Void:
            case DataType.Nil:
                return LuaVariableType.TYPE_NULL;

            default:
                return LuaVariableType.TYPE_OBJECT;
        }
    }

    public static object GetValue(DynValue val)
    {
        object result;

        if (val == null) return null;

        DataType valType = val.Type;

        switch (valType)
        {
            case DataType.Boolean:
                result = val.Boolean;
                break;

            case DataType.Number:
                if (val.Number % 1 == 0)
                {
                    result = (long)val.Number;
                }
                else
                {
                    result = val.Number;
                }
                break;

            case DataType.String:
                result = val.String;
                break;

            case DataType.Tuple:
                int len = val.Tuple.Length;
                result = new List<object>();
                for (int i = 0; i < len; i++)
                {
                    ((List<object>)result).Add(GetValue(val.Tuple[i]));
                }
                break;

            case DataType.Table:
                Dictionary<string, object> dic = null;
                List<object> list = null;
                bool isDic = false;

                foreach (var key in val.Table.Keys)
                {
                    if (key.Type == DataType.String)
                    {
                        if (dic == null) dic = new Dictionary<string, object>();
                        isDic = true;
                        dic.Add(key.String, GetValue(val.Table.Get(key.String)));
                    }
                    else if (!isDic && key.Type == DataType.Number)
                    {
                        if (list == null)
                        {
                            list = new List<object>(val.Table.Length);
                            for (int i = 0; i < val.Table.Length; i++) list.Add(null);
                        }
                        list[(int)key.Number - 1] = GetValue(val.Table.Get((int)key.Number));
                    }
                }
                result = isDic ? dic : list;
                break;

            case DataType.UserData:
                result = val.UserData.Object;
                if (result == null) result = val.UserData.Descriptor.Type;
                break;

            case DataType.Function:
                result = val.Function;
                break;

            case DataType.ClrFunction:
                result = val.Callback;
                break;

            case DataType.Nil:
            case DataType.Void:
                result = null;
                break; 

            default:
                result = val;
                break;
        }

        return result;
    }
}

/// <summary>
/// DynValから変換したデータ型
/// </summary>
public enum LuaVariableType
{
    TYPE_NULL,
    TYPE_OBJECT,
    TYPE_CLROBJECT,
    TYPE_CLRTYPE,
    TYPE_BOOLEAN,
    TYPE_LONG,
    TYPE_DOUBLE,
    TYPE_STRING,
    TYPE_LIST,
    TYPE_DICTIONARY,
    TYPE_FUNCTION,
    TYPE_CALLBACK,
}
```