Skip to content

Mono and IL2CPP ​

Unity compiles a game's C# code with one of two scripting backends, and each mod build targets one of them. The SDK detects the backend from the game folder: GameAssembly.dll next to the executable indicates IL2CPP, and a <game>_Data/Managed folder containing DLLs indicates Mono. Lustral's API is identical on both; the differences concern how mods interact with the game's own code.

MonoIL2CPP
Game code.NET assemblies in <game>_Data/ManagedNative code in GameAssembly.dll
Mods compile againstThe game's assembliesInterop assemblies generated by Lustral in Lustral/interop
Mods run onThe game's Mono runtime.NET 10, shipped by Lustral in Lustral/dotnet
Target framework.NET Standard 2.1.NET 10
Library isolationShared by all mods (single application domain)Per mod (one load context per mod)
DebuggerMono soft debugger (Unity debugger in the IDE)The IDE's .NET debugger

The SDK configures all of the above. Do not set <TargetFramework> in the project. To build a mod for both backends from one project, see Multi-Backend Mods.

Mono Games ​

The game's assemblies are standard .NET assemblies: mods reference their types directly and use regular string, List<T> and array types. Use a decompiler such as ILSpy or dnSpy to browse the game's code.

All mods share the game's runtime, so if two mods ship different versions of the same library, only one version is loaded. Ship libraries only when necessary, or share them through a library mod.

IL2CPP Games ​

IL2CPP games contain no .NET code to reference. On its first launch with Lustral, the game's interop assemblies are generated: .NET assemblies with the same types and members as the game's code, whose methods call into the native code. They are generated by Il2CppInterop into Lustral/interop and regenerated after each game update. Mods compile against these assemblies. Opening Lustral/interop/Assembly-CSharp.dll in ILSpy shows every class, field and method signature of the game (method bodies only contain calls into native code).

Interop types behave much like the game's original types, with the differences described below.

Naming ​

  • Unity types keep their names: UnityEngine.GameObject, UnityEngine.Transform.
  • .NET types used by the game are in the Il2CppSystem namespace: Il2CppSystem.String, Il2CppSystem.Collections.Generic.List<T>, Il2CppSystem.Action.
  • The game's namespaces are prefixed with Il2Cpp: Assets.Scripts.Unity.Player becomes Il2CppAssets.Scripts.Unity.Player, and types without a namespace are placed in Il2Cpp.
csharp
using Il2Cpp;                        // game types without a namespace
using Il2CppAssets.Scripts.Unity;    // the game's Assets.Scripts.Unity namespace

Strings and Primitive Types ​

Game strings are of type Il2CppSystem.String, and many members use Il2CppSystem.Int32, Il2CppSystem.Boolean and similar types. All of them convert implicitly to and from the corresponding .NET types:

csharp
var host = new GameObject("My mod");        // string to Il2CppSystem.String
string name = host.name;                    // Il2CppSystem.String to string
int count = transform.childCount;           // Il2CppSystem.Int32 to int

To use .NET string methods (Contains, Split, formatting), assign the value to a string first.

Collections ​

Game collections are IL2CPP collections: Il2CppSystem.Collections.Generic.List<T>, Dictionary<TKey, TValue>, and arrays of type Il2CppArrayRank1<T>. They provide the usual members (Count, indexers, Add, enumeration), and arrays convert to and from .NET arrays with an explicit cast:

csharp
var vertices = (Vector3[])mesh.vertices;
mesh.vertices = (Il2CppArrayRank1<Vector3>)vertices;

Type Checks and Casts ​

Objects returned by the game are wrapped according to their actual class, so is, as and casts work as usual:

csharp
if (component is Rigidbody body)
    body.isKinematic = true;

Delegates ​

Where the game expects a delegate (an event or callback), pass a .NET delegate; it is converted automatically:

csharp
SceneManager.add_sceneLoaded((Action<Scene, LoadSceneMode>)OnSceneLoaded);

Events are exposed as add_ and remove_ methods on interop types.

Custom Classes ​

Classes that Unity must recognize as components, or that the game calls into, must also exist on the IL2CPP side. See Custom Components.

Stripped Code ​

IL2CPP builds only include the code the game uses; methods, constructors and classes the game never calls are absent. When generating the interop assemblies, Lustral restores as much as possible ("unstripping") from Unity's own libraries for the game's exact Unity version, which are downloaded once per Unity version from Lustral.UnityLibraries. This restores Unity's C# code removed by the build, such as Vector3, Color and Quaternion operations, Mathf, overloads and helper methods built on members the game includes. The restored members are identical on every user's machine.

The following remain unavailable and are reported by the compiler:

  • Members implemented in the engine's native code that the game does not include, and C# members that depend on them. Il2CppInterop cannot yet restore these reliably, so Lustral omits them rather than generating members that fail at runtime. Lustral's own events, coroutines and asset bundles do not depend on them.
  • Unity classes that the build excludes entirely, such as AssetBundle in many games, and members that use them.
  • Generic methods and collections instantiated with value types the game never uses, such as a List<T> of a struct the game never stores in a list.

If a required member is unavailable, look for an alternative in the interop assemblies: another overload, a non-generic variant or a different API. If the libraries cannot be downloaded (no internet connection, or a Unity version that has not been published yet), the interop assemblies are generated without unstripping, and Lustral retries one day later.

Game Updates ​

Lustral detects game updates and regenerates the interop assemblies on the next launch, displaying a progress window. If the game's classes changed, rebuild the mod against the new assemblies; the compiler reports any member that no longer exists.

Mods are third-party software. Lustral does not verify their behavior and is not responsible for any damage they cause.