{"id":25315676,"url":"https://github.com/yn01-dev/lua-csharp","last_synced_at":"2025-02-27T20:14:03.738Z","repository":{"id":258100439,"uuid":"860552002","full_name":"yn01-dev/Lua-CSharp","owner":"yn01-dev","description":"High performance Lua interpreter implemented in C# for .NET and Unity","archived":false,"fork":false,"pushed_at":"2025-01-11T12:13:13.000Z","size":2122,"stargazers_count":304,"open_issues_count":29,"forks_count":13,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-02-20T19:11:16.860Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yn01-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-09-20T16:44:58.000Z","updated_at":"2025-02-20T12:16:25.000Z","dependencies_parsed_at":"2025-01-16T14:15:15.182Z","dependency_job_id":"9812c064-cad8-4062-930a-83758d89693d","html_url":"https://github.com/yn01-dev/Lua-CSharp","commit_stats":null,"previous_names":["annulusgames/lua-csharp","yn01dev/lua-csharp","yn01-dev/lua-csharp"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yn01-dev%2FLua-CSharp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yn01-dev%2FLua-CSharp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yn01-dev%2FLua-CSharp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yn01-dev%2FLua-CSharp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yn01-dev","download_url":"https://codeload.github.com/yn01-dev/Lua-CSharp/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241054826,"owners_count":19901504,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-13T18:10:38.376Z","updated_at":"2025-02-27T20:14:03.711Z","avatar_url":"https://github.com/yn01-dev.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Lua-CSharp\n\nHigh performance Lua interpreter implemented in C# for .NET and Unity\n\n![img](docs/Header.png)\n\n[![NuGet](https://img.shields.io/nuget/v/LuaCSharp.svg)](https://www.nuget.org/packages/LuaCSharp)\n[![Releases](https://img.shields.io/github/release/AnnulusGames/Lua-CSharp.svg)](https://github.com/AnnulusGames/Lua-CSharp/releases)\n[![license](https://img.shields.io/badge/LICENSE-MIT-green.svg)](LICENSE)\n\nEnglish | [日本語](README_JA.md)\n\n## Overview\n\nLua-CSharp is a library that provides a Lua interpreter implemented in C#. By integrating Lua-CSharp, you can easily embed Lua scripts into your .NET applications.\n\nLua-CSharp leverages the latest C# features, designed with low allocation and high performance in mind. It is optimized to deliver maximum performance when used for interoperation between C# and Lua in C# applications. Below is a benchmark comparison with [MoonSharp](https://github.com/moonsharp-devs/moonsharp/) and [NLua](https://github.com/NLua/NLua):\n\n![img](docs/Benchmark.png)\n\nMoonSharp generally provides good speed but incurs significant allocations due to its design. NLua, being a C-binding implementation, is fast, but introduces substantial overhead when interacting with the C# layer. Lua-CSharp, fully implemented in C#, allows for seamless interaction with C# code without additional overhead. Moreover, it operates reliably in AOT environments since it does not rely on IL generation.\n\n## Features\n\n* Lua 5.2 interpreter implemented in C#\n* Easy-to-use API integrated with async/await\n* Support for exception handling with try-catch\n* High-performance implementation utilizing modern C#\n* Unity support (works with both Mono and IL2CPP)\n\n## Installation\n\n### NuGet packages\n\nTo use Lua-CSharp, .NET Standard 2.1 or higher is required. The package can be obtained from NuGet.\n\n### .NET CLI\n\n```ps1\ndotnet add package LuaCSharp\n```\n\n### Package Manager\n\n```ps1\nInstall-Package LuaCSharp\n```\n\n### Unity\n\nYou can also use Lua-CSharp with Unity. For details, see the [Lua.Unity](#luaunity) section.\n\n## Quick Start\n\nBy using the `LuaState` class, you can execute Lua scripts from C#. Below is a sample code that evaluates a simple calculation written in Lua.\n\n```cs\nusing Lua;\n\n// Create a LuaState\nvar state = LuaState.Create();\n\n// Execute a Lua script string with DoStringAsync\nvar results = await state.DoStringAsync(\"return 1 + 1\");\n\n// 2\nConsole.WriteLine(results[0]);\n```\n\n\u003e [!WARNING]\n\u003e `LuaState` is not thread-safe. Do not access it from multiple threads simultaneously.\n\n## LuaValue\n\nValues in Lua scripts are represented by the `LuaValue` type. The value of a `LuaValue` can be read using `TryRead\u003cT\u003e(out T value)` or `Read\u003cT\u003e()`.\n\n```cs\nvar results = await state.DoStringAsync(\"return 1 + 1\");\n\n// double\nvar value = results[0].Read\u003cdouble\u003e();\n```\n\nYou can also get the type of the value from the `Type` property.\n\n```cs\nvar isNil = results[0].Type == LuaValueType.Nil;\n```\n\nBelow is a table showing the type mapping between Lua and C#.\n\n| Lua        | C#                |\n| ---------- | ----------------- |\n| `nil`      | `LuaValue.Nil`    |\n| `boolean`  | `bool`            |\n| `string`   | `string`          |\n| `number`   | `double`, `float` |\n| `table`    | `LuaTable`        |\n| `function` | `LuaFunction`     |\n| `userdata` | `LuaUserData`     |\n| `thread`   | `LuaThread`       |\n\nWhen creating a `LuaValue` from the C# side, compatible types are implicitly converted into `LuaValue`.\n\n```cs\nLuaValue value;\nvalue = 1.2;           // double   -\u003e  LuaValue\nvalue = \"foo\";         // string   -\u003e  LuaValue\nvalue = new LuaTable() // LuaTable -\u003e  LuaValue\n```\n\n## LuaTable\n\nLua tables are represented by the `LuaTable` type. They can be used similarly to `LuaValue[]` or `Dictionary\u003cLuaValue, LuaValue\u003e`.\n\n```cs\n// Create a table in Lua\nvar results = await state.DoStringAsync(\"return { a = 1, b = 2, c = 3 }\");\nvar table1 = results[0].Read\u003cLuaTable\u003e();\n\n// 1\nConsole.WriteLine(table1[\"a\"]);\n\n// Create a table in C#\nresults = await state.DoStringAsync(\"return { 1, 2, 3 }\");\nvar table2 = results[0].Read\u003cLuaTable\u003e();\n\n// 1 (Note: Lua arrays are 1-indexed)\nConsole.WriteLine(table2[1]);\n```\n\n## Global Environment\n\nYou can access Lua's global environment through `state.Environment`. This table allows for easy value exchange between Lua and C#.\n\n```cs\n// Set a = 10\nstate.Environment[\"a\"] = 10;\n\nvar results = await state.DoStringAsync(\"return a\");\n\n// 10\nConsole.WriteLine(results[0]);\n```\n\n## Standard Libraries\n\nYou can use Lua's standard libraries as well. By calling `state.OpenStandardLibraries()`, the standard library tables are added to the `LuaState`.\n\n```cs\nusing Lua;\nusing Lua.Standard;\n\nvar state = LuaState.Create();\n\n// Add standard libraries\nstate.OpenStandardLibraries();\n\nvar results = await state.DoStringAsync(\"return math.pi\");\nConsole.WriteLine(results[0]); // 3.141592653589793\n```\n\nFor more details on standard libraries, refer to the [Lua official manual](https://www.lua.org/manual/5.2/manual.html#6).\n\n\u003e [!WARNING]\n\u003e Lua-CSharp does not support all functions of the standard libraries. For details, refer to the [Compatibility](#compatibility) section.\n\n## Functions\n\nLua functions are represented by the `LuaFunction` type. With `LuaFunction`, you can call Lua functions from C#, or define functions in C# that can be called from Lua.\n\n### Calling Lua Functions from C#\n\n```lua\n-- lua2cs.lua\n\nlocal function add(a, b)\n    return a + b\nend\n\nreturn add;\n```\n\n```cs\nvar results = await state.DoFileAsync(\"lua2cs.lua\");\nvar func = results[0].Read\u003cLuaFunction\u003e();\n\n// Execute the function with arguments\nvar funcResults = await func.InvokeAsync(state, new[] { 1, 2 });\n\n// 3\nConsole.WriteLine(funcResults[0]);\n```\n\n### Calling C# Functions from Lua\n\nIt is possible to create a `LuaFunction` from a lambda expression.\n\n```cs\n// Add the function to the global environment\nstate.Environment[\"add\"] = new LuaFunction((context, buffer, ct) =\u003e\n{\n    // Get the arguments using context.GetArgument\u003cT\u003e()\n    var arg0 = context.GetArgument\u003cdouble\u003e(0);\n    var arg1 = context.GetArgument\u003cdouble\u003e(1);\n\n    // Write the return value to the buffer\n    buffer.Span[0] = arg0 + arg1;\n\n    // Return the number of values\n    return new(1);\n});\n\n// Execute a Lua script\nvar results = await state.DoFileAsync(\"cs2lua.lua\");\n\n// 3\nConsole.WriteLine(results[i]);\n```\n\n```lua\n-- cs2lua.lua\n\nreturn add(1, 2)\n```\n\n\u003e [!TIP]  \n\u003e Defining functions with `LuaFunction` can be somewhat verbose. When adding multiple functions, it is recommended to use the Source Generator with the `[LuaObject]` attribute. For more details, see the [LuaObject](#luaobject) section.\n\n## Integration with async/await\n\n`LuaFunction` operates asynchronously. Therefore, you can define a function that waits for an operation in Lua, such as the example below:\n\n```cs\n// Define a function that waits for the given number of seconds using Task.Delay\nstate.Environment[\"wait\"] = new LuaFunction(async (context, buffer, ct) =\u003e\n{\n    var sec = context.GetArgument\u003cdouble\u003e(0);\n    await Task.Delay(TimeSpan.FromSeconds(sec));\n    return 0;\n});\n\nawait state.DoFileAsync(\"sample.lua\");\n```\n\n```lua\n-- sample.lua\n\nprint \"hello!\"\n\nwait(1.0) -- wait 1 sec\n\nprint \"how are you?\"\n\nwait(1.0) -- wait 1 sec\n\nprint \"goodbye!\"\n```\n\nThis code can resume the execution of the Lua script after waiting with await, as shown in the following figure. This is very useful when writing scripts to be incorporated into games.\n\n![img](docs/img1.png)\n\n## Coroutines\n\nLua coroutines are represented by the `LuaThread` type.\n\nCoroutines can not only be used within Lua scripts, but you can also await Lua-created coroutines from C#.\n\n```lua\n-- coroutine.lua\n\nlocal co = coroutine.create(function()\n    for i = 1, 10 do\n        print(i)\n        coroutine.yield()\n    end\nend)\n\nreturn co\n```\n\n```cs\nvar results = await state.DoFileAsync(\"coroutine.lua\");\nvar co = results[0].Read\u003cLuaThread\u003e();\n\nfor (int i = 0; i \u003c 10; i++)\n{\n    var resumeResults = await co.ResumeAsync(state);\n\n    // Similar to coroutine.resume(), returns true on success and the return values afterward\n    // 1, 2, 3, 4, ...\n    Console.WriteLine(resumeResults[1]);\n}\n```\n\n## LuaObject\n\nBy applying the `[LuaObject]` attribute, you can create custom classes that run within Lua. Adding this attribute to a class that you wish to use in Lua allows the Source Generator to automatically generate the code required for interaction from Lua.\n\nThe following is an example implementation of a wrapper class for `System.Numerics.Vector3` that can be used in Lua:\n\n```cs\nusing System.Numerics;\nusing Lua;\n\nvar state = LuaState.Create();\n\n// Add an instance of the defined LuaObject as a global variable\n// (Implicit conversion to LuaValue is automatically defined for classes with the LuaObject attribute)\nstate.Environment[\"Vector3\"] = new LuaVector3();\n\nawait state.DoFileAsync(\"vector3_sample.lua\");\n\n// Add LuaObject attribute and partial keyword\n[LuaObject]\npublic partial class LuaVector3\n{\n    Vector3 vector;\n\n    // Add LuaMember attribute to members that will be used in Lua\n    // The argument specifies the name used in Lua (if omitted, the member name is used)\n    [LuaMember(\"x\")]\n    public float X\n    {\n        get =\u003e vector.X;\n        set =\u003e vector = vector with { X = value };\n    }\n\n    [LuaMember(\"y\")]\n    public float Y\n    {\n        get =\u003e vector.Y;\n        set =\u003e vector = vector with { Y = value };\n    }\n\n    [LuaMember(\"z\")]\n    public float Z\n    {\n        get =\u003e vector.Z;\n        set =\u003e vector = vector with { Z = value };\n    }\n\n    // Static methods are treated as regular Lua functions\n    [LuaMember(\"create\")]\n    public static LuaVector3 Create(float x, float y, float z)\n    {\n        return new LuaVector3()\n        {\n            vector = new Vector3(x, y, z)\n        };\n    }\n\n    // Instance methods implicitly receive the instance (this) as the first argument\n    // In Lua, this is accessed with instance:method() syntax\n    [LuaMember(\"normalized\")]\n    public LuaVector3 Normalized()\n    {\n        return new LuaVector3()\n        {\n            vector = Vector3.Normalize(vector)\n        };\n    }\n}\n```\n\n```lua\n-- vector3_sample.lua\n\nlocal v1 = Vector3.create(1, 2, 3)\n-- 1  2  3\nprint(v1.x, v1.y, v1.z)\n\nlocal v2 = v1:normalized()\n-- 0.26726123690605164  0.5345224738121033  0.8017836809158325\nprint(v2.x, v2.y, v2.z)\n```\n\nThe types of fields/properties with the `[LuaMember]` attribute, as well as the argument and return types of methods, must be either `LuaValue` or convertible to/from `LuaValue`.\n\nReturn types such as `void`, `Task/Task\u003cT\u003e`, `ValueTask/ValueTask\u003cT\u003e`, `UniTask/UniTask\u003cT\u003e`, and `Awaitable/Awaitable\u003cT\u003e` are also supported.\n\nIf the type is not supported, the Source Generator will output a compile-time error.\n\n### LuaMetamethod\n\nBy adding the `[LuaMetamethod]` attribute, you can designate a C# method to be used as a Lua metamethod.\n\nHere is an example that adds the `__add`, `__sub`, and `__tostring` metamethods to the `LuaVector3` class:\n\n```cs\n[LuaObject]\npublic partial class LuaVector3\n{\n    // The previous implementation is omitted\n\n    [LuaMetamethod(LuaObjectMetamethod.Add)]\n    public static LuaVector3 Add(LuaVector3 a, LuaVector3 b)\n    {\n        return new LuaVector3()\n        {\n            vector = a.vector + b.vector\n        };\n    }\n    \n    [LuaMetamethod(LuaObjectMetamethod.Sub)]\n    public static LuaVector3 Sub(LuaVector3 a, LuaVector3 b)\n    {\n        return new LuaVector3()\n        {\n            vector = a.vector - b.vector\n        };\n    }\n\n    [LuaMetamethod(LuaObjectMetamethod.ToString)]\n    public override string ToString()\n    {\n        return vector.ToString();\n    }\n}\n```\n\n```lua\nlocal v1 = Vector3.create(1, 1, 1)\nlocal v2 = Vector3.create(2, 2, 2)\n\nprint(v1) -- \u003c1, 1, 1\u003e\nprint(v2) -- \u003c2, 2, 2\u003e\n\nprint(v1 + v2) -- \u003c3, 3, 3\u003e\nprint(v1 - v2) -- \u003c-1, -1, -1\u003e\n```\n\n\u003e [!NOTE]  \n\u003e `__index` and `__newindex` cannot be set as they are used internally by the code generated by `[LuaObject]`.\n\n## Module Loading\n\nIn Lua, you can load modules using the `require` function. In regular Lua, modules are managed by searchers within the `package.searchers` function list. In Lua-CSharp, this is replaced by the `ILuaModuleLoader` interface.\n\n```cs\npublic interface ILuaModuleLoader\n{\n    bool Exists(string moduleName);\n    ValueTask\u003cLuaModule\u003e LoadAsync(string moduleName, CancellationToken cancellationToken = default);\n}\n```\n\nYou can set the `LuaState.ModuleLoader` to change how modules are loaded. By default, the `FileModuleLoader` is set to load modules from Lua files.\n\nYou can also combine multiple loaders using `CompositeModuleLoader.Create(loader1, loader2, ...)`.\n\n```cs\nstate.ModuleLoader = CompositeModuleLoader.Create(\n    new FileModuleLoader(),\n    new CustomModuleLoader()\n);\n```\n\nLoaded modules are cached in the `package.loaded` table, just like regular Lua. This can be accessed via `LuaState.LoadedModules`.\n\n## Exception Handling\n\nLua script parsing errors and runtime exceptions throw exceptions that inherit from `LuaException`. You can catch these to handle errors during execution.\n\n```cs\ntry\n{\n    await state.DoFileAsync(\"filename.lua\");\n}\ncatch (LuaParseException)\n{\n    // Handle parsing errors\n}\ncatch (LuaRuntimeException)\n{\n    // Handle runtime exceptions\n}\n```\n\n## Lua.Unity\n\nLua-CSharp can also be used in Unity (works with both Mono and IL2CPP).\n\n### Requirements\n\n* Unity 2021.3 or higher\n\n### Installation\n\n1. Install [NugetForUnity](https://github.com/GlitchEnzo/NuGetForUnity).\n\n2. Open the NuGet window by going to `NuGet \u003e Manage NuGet Packages`, search for the `LuaCSharp` package, and install it.\n\n3. Open the Package Manager window by selecting `Window \u003e Package Manager`, then click on `[+] \u003e Add package from git URL` and enter the following URL:\n\n    ```\n    https://github.com/AnnulusGames/Lua-CSharp.git?path=src/Lua.Unity/Assets/Lua.Unity\n    ```\n\n### LuaImporter / LuaAsset\n\nBy introducing Lua.Unity, files with the `.lua` extension can be treated as `LuaAsset`.\n\n![img](docs/img-lua-importer.png)\n\nThese assets can be used similarly to a standard `TextAsset`.\n\n```cs\nvar asset = Resources.Load\u003cLuaAsset\u003e(\"example\");\nawait state.DoStringAsync(asset.Text, ct);\n```\n\n### Resources(Addressables)ModuleLoader\n\nImplementations of `ILuaModuleLoader` that utilize either Resources or Addressables internally are also provided.\n\n```cs\n// Use Resources for module loading\nstate.ModuleLoader = new ResourcesModuleLoader();\n\n// Use Addressables for module loading (requires the Addressables package)\nstate.ModuleLoader = new AddressablesModuleLoader();\n```\n\n## Compatibility\n\nLua-CSharp is designed with integration into .NET in mind, so there are several differences from the C implementation.\n\n### Binary\n\nLua-CSharp does not support Lua bytecode (tools like `luac` cannot be used). Only Lua source code can be executed.\n\n### Character Encoding\n\nThe character encoding used in Lua-CSharp is UTF-16. Since standard Lua assumes a single-byte character encoding, string behavior differs significantly.\n\nFor example, in regular Lua, the following code outputs `15`, but in Lua-CSharp, it outputs `5`.\n\n```lua\nlocal l = string.len(\"あいうえお\")\nprint(l)\n```\n\nAll string library functions handle strings as UTF-16.\n\n### Garbage Collection\n\nSince Lua-CSharp is implemented in C#, it relies on .NET's garbage collector. As a result, memory management behavior differs from regular Lua.\n\nWhile `collectgarbage()` is available, it simply calls the corresponding .NET garbage collection method and may not exhibit the same behavior as C's Lua garbage collector.\n\n## License\n\nLua-CSharp is licensed under the [MIT License](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyn01-dev%2Flua-csharp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyn01-dev%2Flua-csharp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyn01-dev%2Flua-csharp/lists"}