Darp.Luau
0.2.0
dotnet add package Darp.Luau --version 0.2.0
NuGet\Install-Package Darp.Luau -Version 0.2.0
<PackageReference Include="Darp.Luau" Version="0.2.0" />
<PackageVersion Include="Darp.Luau" Version="0.2.0" />
<PackageReference Include="Darp.Luau" />
paket add Darp.Luau --version 0.2.0
#r "nuget: Darp.Luau, 0.2.0"
#:package Darp.Luau@0.2.0
#addin nuget:?package=Darp.Luau&version=0.2.0
#tool nuget:?package=Darp.Luau&version=0.2.0
Darp.Luau
Darp.Luau is a .NET wrapper around Luau focused on native AOT compatibility, typed value access, and explicit ownership for Luau-backed references.
Why another lua library
- NativeAOT first
- Typed reads and writes for tables, functions, userdata, strings, and buffers
- Clear lifetime guarantees both stability and performance
- Simple API through source-generated interceptors
- Custom libraries and managed userdata
- Support for
linux,windows,macoson bothx64,arm64
Quick start
using Darp.Luau;
using var lua = new LuauState();
using LuauFunction log = lua.CreateFunction((string message) => Console.WriteLine(message));
lua.Globals.Set("log", log);
using LuauTable config = lua.CreateTable();
config.Set("name", "Ada");
config.Set("enabled", true);
lua.Globals.Set("config", config);
lua.Load(
"""
function add(a, b)
return a + b
end
log(config.name)
result = add(20, 22)
"""
).Execute();
double result = lua.Globals.GetNumber("result");
Call Lua functions from C#
using LuauFunction add = lua.Globals.GetLuauFunction("add");
double sum = add.Invoke<double>(1, 2);
using LuauFunction pair = lua.Globals.GetLuauFunction("pair");
(int total, int delta) = pair.Invoke<int, int>(20, 4);
Invoke<TR>(...) converts a single Luau return value to the managed type you ask for and ignores extras. Use Invoke<TR1, TR2>(...), ... for typed multi-return calls, and InvokeMulti(...) for raw LuauValue[] access. The current argument buffer accepts up to 4 arguments per call.
Load(...).Execute(...) follows the same return-shaping pattern for chunk execution: use Load(...).Execute<TR>() for the first typed return value, Load(...).Execute<TR1, TR2>(), ... for typed multi-return calls, and Load(...).ExecuteMulti() for raw LuauValue[] access.
(int total, int delta) = lua.Load("return 20, 4").Execute<int, int>();
Expose managed callbacks
Use CreateFunction(...) for supported fixed delegate signatures:
using LuauFunction sum = lua.CreateFunction((int a, int b) => a + b);
lua.Globals.Set("sum", sum);
using LuauFunction pair = lua.CreateFunction((int a, int b) => (a + b, a - b));
lua.Globals.Set("pair", pair);
Use CreateFunctionBuilder(...) when you need manual argument parsing, explicit user-facing errors, or a callback shape that the generator-backed path does not support:
using LuauFunction pair = lua.CreateFunctionBuilder(static args =>
{
if (!args.TryValidateArgumentCount(2, out string? error))
return LuauReturn.Error(error);
if (!args.TryReadNumber(1, out int a, out error) || !args.TryReadNumber(2, out int b, out error))
return LuauReturn.Error(error);
if (a <= b)
return LuauReturn.Error("Expected a to be greater than b");
return LuauReturn.Ok(a + b, a - b);
});
CreateFunction(...) must be called directly at the call site so the generator can intercept it. It supports fixed delegate signatures, including supported top-level tuple returns. If you need a shape that is not supported there, use CreateFunctionBuilder(...).
Work with tables
using LuauTable settings = lua.CreateTable();
settings.Set("volume", 0.8);
settings.Set("muted", false);
settings.Set("blob", new byte[] { 1, 2, 3 });
lua.Globals.Set("settings", settings);
using LuauTable roundTripped = lua.Globals.GetLuauTable("settings");
double volume = roundTripped.GetNumber("volume");
bool muted = roundTripped.GetBoolean("muted");
byte[] blob = roundTripped.GetBuffer("blob");
Use Get* for required values, TryGet* for optional or external data, and *OrNil when nil is part of the contract.
Work with userdata
var player = new PlayerUserdata { Name = "Ada" };
lua.Globals.Set("player", IntoLuau.FromUserdata(player));
PlayerUserdata samePlayer = lua.Globals.GetUserdata<PlayerUserdata>("player");
using LuauUserdata playerRef = lua.Globals.GetLuauUserdata("player");
_ = playerRef.TryGetManaged(out PlayerUserdata? resolvedPlayer, out string? error);
Managed userdata types implement ILuauUserData<T> to expose script-facing fields, setters, and methods. See Userdata for the full hook model.
CreateFunction(...) also supports managed userdata parameters and returns for types that implement ILuauUserData<TSelf>.
Register custom libraries
lua.OpenLibrary("game", static (state, in LuauTable lib) =>
{
lib.Set("answer", 42);
using LuauFunction add = state.CreateFunction((int a, int b) => a + b);
lib.Set("add", add);
});
OpenLibrary(...) registers a global table. It is a convenient way to expose host-provided APIs, but it is not a require(...)-style module loader by itself.
Ownership and lifetime
LuauTable,LuauFunction,LuauString,LuauBuffer,LuauUserdata, and reference-backedLuauValueare owned references and should be disposed.LuauTableView,LuauFunctionView,LuauStringView,LuauBufferView,LuauUserdataView, andLuauArgsare borrowed callback-scoped values.- Reference-backed values belong to one
LuauState; cross-state usage is invalid.
Current boundaries
Load(...).Execute(...)is the script execution API today. If you want file-based execution, read the file yourself and pass its contents in.CreateFunction(...)is generator-backed and has no runtime fallback.LuauStateis not thread-safe.- A documented module system and higher-level async/thread orchestration are not part of the current surface yet.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Darp.Luau.Native (>= 0.4.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.