# LuaInterface and Coroutines

This topic is 2655 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I've spent the last two days working on this and I've finally cobbled together a mediocre solution.

To start-off, I have 5 coroutine managing functions:
function SetupCoroutineTable()    coroutine_table = { };endfunction AddCoroutine(Item)    table.insert(coroutine_table, Item);endfunction RemoveCoroutine(Index)    table.remove(coroutine_table, Index);endfunction ResumeCoroutine(Index)    Item = coroutine_table[Index];    if coroutine.status(Item) ~= 'dead' then        coroutine.resume(Item);    endendfunction ResumeAllCoroutines()    for Index,Item in ipairs(coroutine_table) do        ResumeCoroutine(Index);    endend

I call SetupCoroutineTable at the start of my program. The major issue I see here is that I don't manage the table at all (just keep inserting and only remove when told to.) I could add a managing function that clears the dead coroutines, but this is the least of my concerns right now.

Now for my sample. The main idea behind this is to use for scripting scenes (move player here, show message, do this, etc.) So, I went with counting.

-- the function that my code calls. -- Would be somthing like an event's OnActivate function.function foo()    count(10);    display('Finished counting to 10.\n');    count(21);    display('Finished counting to 21.\n');end-- This is the count function called above.function count(value)    set_value(0); -- C# method    count_to(value); -- C# method    while get_value() < value do -- get_value() is another C# method        coroutine.yield();    endend

This is one of my major issue: I don't want to have to write systems like this for everything that I want the script to be able to wait on (i.e. ideally count would be in the actual code.)

Now for the ugly part. I have a method in C#:
public void CallScriptFunction(string Name){    luaVM.DoString(@"local coro = coroutine.create(" + Name + ");    InsertCoroutine(coro);    coroutine.resume(coro);");}

The main issues being that: it's very hacked together and it's not flexible (doesn't support calling a method with arguments.)

What I'm wondering is if anyone has any insight into a better solution (or some suggestions to improve what I have.)

Edited at 4:25am
I just got done trying a new system. It uses the main portion, but is more flexible. I implemented a class named WaitInfo which contains a delegate that returns a bool. This is returned from any method I want to allow to wait. Then, in my lua code, I pass the WaitInfo to my wait function. Here's the revised code

public delegate bool WaitPredicate();public sealed class WaitInfo{    WaitPredicate predicate;    public WaitInfo(WaitPredicate Predicate)    {        this.predicate = Predicate;    }    public bool IsFinished()    {        return predicate();    }}public WaitInfo CountTo(int Value){    TaskManager.Add(new CountTask((task) =>    {        ++countValue;        Output += countValue.ToString() + " ";        if(countValue >= Value)            task.Kill();    }));    return new WaitInfo(new WaitPredicate(() => { return countValue >= Value; }));}

The new WaitInfo class and CountTo method (note that I tried just returning a WaitPredicate, but I can't get Lua to call it. I don't get any errors, it's just never called. I was thinking maybe I need to call Invoke or something, but I didn't try it.)

function wait(winfo)    while not winfo:IsFinished() do        coroutine.yield();    endendfunction foo()    local winfo = count_to(10);    wait(winfo);    display('finished the first count!\n');    winfo = count_to(21);    wait(winfo);    display('finished the second count!\n');end

The new wait function and update foo function.

This new system is much better than what I had, but I'm still looking for suggestions; especially on the coroutine-wrapping code I've implemented (read: the major hack I have in place.)

Edited at 4:56am
Ok, I just went back through and yes, I can simply return a WaitPredicate and then in wait I just call winfo:Invoke().

Also, is using DoString() as I am even stable? If so, I could omit the wait function and set it up using DoString() straight from the methods (i.e. inside of CountTo and such.)

I'd also like to note that a lot of code is just for testing this (like the CountTo method's code and what-not.) I'm just trying to get the system nailed down.

[Edited by - Programmer16 on October 17, 2010 4:01:55 AM]

##### Share on other sites
I'm going to stick with this for now (don't let this put you off from posting though; I'm still more than interested in comments and suggestions.) It's not the best solution, but it's working really well and I want to move onto other stuff.

Here's the final solution I have setup:
_coroutines = { };function _coroutines_Start(Item)    local Coro = coroutine.create(Item);    for Index, Value in ipairs(_coroutines) do        if coroutine.status(Value) == 'dead' then            _coroutines[Index] = Coro;            coroutine.resume(Coro);            return;        end    end    table.insert(_coroutines, Coro);    coroutine.resume(Coro);endfunction _coroutines_Update()    for Index, Value in ipairs(_coroutines) do        if coroutine.status(Value) ~= 'dead' then            coroutine.resume(Value);        end    endendfunction wait(...)	    for Index, Value in ipairs(arg) do        if type(Value) == 'number' then            Predicate = Sleep(Value);            while Predicate:Invoke() do                coroutine.yield();            end        else            while value:Invoke() do                coroutine.yield();            end        end    endend

Then for my C# code:
// Store the _coroutines_Start and _coroutines_Update methodsLuaFunction startFunction, updateFunction;// During initializationstartFunction = LuaVM.GetFunction("_coroutines_Start");updateFunction = LuaVM.GetFunction("_coroutines_Update");void StartCoroutine(Lua LuaVM, string FunctionPath){    LuaFunction = LuaVM.GetFunction(FunctionPath);    startFunction.Call(LuaFunction);}// During updateupdateFunction.Call(new object[] {});// Then, to start a coroutine, I just call StartCoroutine. For the example post initially:StartCoroutine(LuaVM, "foo");

The only major kink now is that I cannot use this system with script functions that require arguments or are part of a table. The only way I can see around this would be:
+ Modify _coroutines_Start to take a table of arguments. Use unpack to pass them to coroutine.resume.
+ Modify StartCoroutines to take a list of arguments. Inside of the method retrieve the table that the method is attached to (would require some string parsing to retrieve the table's name.)

I don't know if this would actually work, but it's the only thing I can come up with at the moment. It's not an issue right now though, so I'll worry about it later.