Skip to content

Multi-Backend Mods ​

Some mods are not specific to one game, for example a free camera, a frame rate counter or a debug console. Such mods can support every Unity game, Mono and IL2CPP alike, by meeting two conditions:

  1. No game requirement. A mod without a ModGame entry loads in any game.
  2. A build for each backend, produced from a single project as described below.

Project Setup ​

Mono and IL2CPP mods are separate builds: Mono mods target .NET Standard 2.1 and compile against the game's assemblies, while IL2CPP mods target .NET 10 and compile against interop assemblies. A single assembly cannot serve both, but a single project can produce both builds. Instead of LustralGame, specify one game of each kind:

xml
<PropertyGroup>
  <LustralMonoGame>PlagueInc</LustralMonoGame>
  <LustralIl2CppGame>BloonsTD6</LustralIl2CppGame>
  <ModId>you.fpscounter</ModId>
</PropertyGroup>

Alternatively, create the project from the template with both options:

sh
dotnet new lustral-mod -n FpsCounter --mono-game PlagueInc --il2cpp-game BloonsTD6 --id you.fpscounter

The SDK builds the project once per backend from the same source:

Mono buildIL2CPP build
Target frameworknetstandard2.1net10.0
Compiles againstThe assemblies of LustralMonoGameThe interop assemblies of LustralIl2CppGame
Compilation symbolLUSTRAL_MONOLUSTRAL_IL2CPP
Outputbin/<configuration>/netstandard2.1/bin/<configuration>/net10.0/
Deployed toThe Mods folder of LustralMonoGameThe Mods folder of LustralIl2CppGame

Each build generates its own mod.toml, which declares the backend it targets. The two games are used for building and testing; the resulting builds run in any other game of the same kind.

Building and Debugging ​

  • dotnet build builds both backends. -p:LustralBackend=Mono or -p:LustralBackend=Il2Cpp builds only one.
  • In Visual Studio or Rider, building the project builds both backends. The editor's target framework selector determines which build IntelliSense uses (netstandard2.1 for Mono, net10.0 for IL2CPP), so both sides of an #if can be inspected.
  • F5 offers a launch profile for each game. Debugging works as it does for single-backend mods.
  • Machine-specific paths belong in Game.props.user, as <LustralMonoGameDir> and <LustralIl2CppGameDir>.

Backend-Specific Code ​

Lustral's API is identical on both backends, as is most Unity code: transform.position, GameObject.Find("Player"), Time.deltaTime and new GameObject("My mod") compile for both. On IL2CPP, strings and primitive types convert implicitly (see Mono and IL2CPP). Where the backends differ, use conditional compilation:

csharp
#if LUSTRAL_IL2CPP
[InjectedType]
public partial class FrameCounter : MonoBehaviour
{
    [Il2CppField]
    public partial Il2CppSystem.Int32 Frames { get; set; }

    [Il2CppMethod]
    private void Update() => Frames = Frames + 1;
}
#else
public class FrameCounter : MonoBehaviour
{
    private int _frames;

    private void Update() => _frames++;
}
#endif

Code most commonly differs in:

  • Custom components: plain classes on Mono, injected types on IL2CPP.
  • Game collections: List<T> on Mono, Il2CppSystem.Collections.Generic.List<T> on IL2CPP.
  • Delegates passed to the game: on IL2CPP, events are exposed as add_ and remove_ methods (see Delegates).

Compatibility With Other Games ​

The mod is compiled against two specific games but runs in games built with other Unity versions and, on IL2CPP, with different parts of Unity stripped from the build. If a game lacks a Unity member the mod uses, a MissingMethodException or TypeLoadException is thrown when that code runs. Prefer commonly used Unity APIs; when using less common ones, check for their presence first (through reflection or exception handling), and use Game.UnityVersion and Game.Backend to determine the environment.

Publishing ​

With ModPackage enabled, a Release build produces one package per backend:

txt
you.fpscounter package -> C:\...\bin\Release\you.fpscounter-1.0.0-mono.zip (SHA-256 ...)
you.fpscounter package -> C:\...\bin\Release\you.fpscounter-1.0.0-il2cpp.zip (SHA-256 ...)

Attach both packages to the GitHub release. Lustral's automatic updates select the package matching the game's backend and never the other. Instruct users to download the package matching their game, as with Lustral itself.

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