Il2Cpp and Mono modding share many similarities, but there are some differences that you should be aware of when modding main branch Schedule I.

Assemblies

In Mono, you can directly reference

using FishNet;
using ScheduleOne.PlayerScripts;

This part isn’t so different in Il2Cpp, but some assemblies (generated by Il2CppAssemblyGenerator) are prefixed with Il2Cpp in the name, so you need to reference them like this:

using Il2CppFishNet;
using Il2CppScheduleOne.PlayerScripts;

Types

Similarly, some methods might use Il2Cpp types instead of regular C# types. For example, Il2CppSystem.Collections.Generic.List<T> instead of System.Collections.Generic.List<T>.

Il2CppSystem.Collections.Generic.List<int> list;
list.FirstOrDefault(); // This won't work
list._items.FirstOrDefault(); // This will work

If you want to grab the IL2CPP type of a class, you can use Il2CppType.Of<T>() method. For example:

using Il2CppInterop.Runtime;
Resources.FindObjectsOfTypeAll(Il2CppType.Of<Camera>());

Casting

You’ll often need to cast types. While in Mono the compiler can infer the type, eg. when creating a callback action, in Il2Cpp you need to specify the type explicitly:

addButton.onClick.AddListener((UnityAction)(() => {
 // Do something
}));

For types with no explicit cast, use .Cast<T>() or .TryCast<T>() method

Type result = il2CppObj.Cast<Type>();

Custom Components

If you want to create a custom component or inherit a Il2Cpp class, you need to use the RegisterTypeInIl2Cpp attribute. This is necessary for Il2Cpp to recognize your class and allow it to be used in the game.

using Il2CppInterop.Runtime;
using Il2CppScheduleOne;

[RegisterTypeInIl2Cpp]
public class MyCommand : ConsoleCommand
{
    public MyCommand(IntPtr ptr) : base(ptr)
    {
    }

    // This constructor is required if we're instatiating from Mono side like
    // new MyCommand();
    // Not needed if not instantiating - don't use this constructor for MonoBehaviours
    public MyCommand() : base(ClassInjector.DerivedConstructorPointer<MyCommand>())
    {
        ClassInjector.DerivedConstructorBody(this);
    }

// Other members required by ConsoleCommand, omitting for brevity
}

Coroutines

Use MelonCoroutines.

using UnityEngine;
using MelonLoader;
public IEnumerator MyCoroutine()
{
    yield return new WaitForSeconds(1f);
    // Do something
}
MelonCoroutines.Start(MyCoroutine());

Supporting both branches

Some modders want to support both branches of Schedule I, Mono and Il2Cpp. This is possible, but requires some extra work.

Git Branches

You can create two branches in your repository, one for Mono and one for Il2Cpp.

Conditional Compilation

You can use conditional compilation to include or exclude code based on the target platform. For example, you can use #if IL2CPP to include code only for Il2Cpp builds, or #if MONO for Mono builds.

#if IL2CPP
using Il2CppScheduleOne;
#else
using ScheduleOne;
#endif
public class MyMod : MelonMod
{
    public override void OnInitializeMelon()
    {
#if IL2CPP
        // Il2Cpp specific code
        MelonLogger.Msg("Running on Il2Cpp");
#else
        // Mono specific code
        MelonLogger.Msg("Running on Mono");
#endif
    }
}

When compiling your mod, you can define the IL2CPP or MONO symbols in the build settings. This will allow you to include or exclude code based on the target platform. Some of the templates shown in Getting Started already have these symbols defined, so you can use them directly.

Last updated 07 Jul 2025, 23:09 +0200 . history