Skip to main content

Lua interop

Lua packages share values through Package.Export, which writes a global that every package can read. Exports is the C# side of it.

Calling Lua from C#​

Lua package
Package.Export("Leaderboard", {
Add = function(name, points) ... end,
Top = function(count) ... end,
})
Exports.Call("Leaderboard.Add", "DasDarki", 10);
var top = Exports.Call<string[]>("Leaderboard.Top", 3);
var table = Exports.Get("Leaderboard") as IDictionary;

Get converts the whole value, so keep a ScriptFunction for hot paths:

var add = Exports.Get<ScriptFunction>("Leaderboard.Add")!;
add.Invoke("DasDarki", 10);

C# callbacks passed to Lua functions are wrapped with ScriptCallback.From:

Exports.Call("Leaderboard.OnChange", ScriptCallback.From((string name) => Log.Info($"{name} changed")));

Calling C# from Lua​

Mark a static class or a static method with [Export]:

[Export("MathLibrary")]
public static class MathLibrary
{
public static int Multiply(int first, int second) => first * second;
}

public static class Shouting
{
[Export]
public static string Shout(string text) => text.ToUpperInvariant();
}
print(MathLibrary.Multiply(6, 7))
print(Shout("hi"))

A static class becomes a table of its public static methods, a method a single global function. Values and delegates can also be exported by code:

Exports.Set("ServerStartedAt", DateTime.UtcNow.ToString("O"));
Exports.Set("Greet", (Func<string, string>)(name => $"hello {name}"));

Exports are removed when their package unloads. Lua packages should look them up when they need them instead of keeping a reference across a hot reload. Call exported functions with a dot, not a colon.