### Download and Run MelonLoader Installer (Windows/Linux)
Source: https://context7.com/lavagang/melonwiki/llms.txt
Use these commands to download and execute the automated installer for MelonLoader on Windows or Linux. The installer simplifies the setup process.
```sh
# Windows — download and run
https://github.com/LavaGang/MelonLoader.Installer/releases/latest/download/MelonLoader.Installer.exe
```
```sh
# Linux — download, mark executable, and run
wget https://github.com/LavaGang/MelonLoader.Installer/releases/latest/download/MelonLoader.Installer.Linux
chmod +x MelonLoader.Installer.Linux
./MelonLoader.Installer.Linux
```
--------------------------------
### Install .NET 6.0 Runtime on Arch Linux (AUR)
Source: https://github.com/lavagang/melonwiki/blob/master/docs/gettingstarted.md
Install the .NET 6.0 Runtime for Il2Cpp-based games on Arch Linux using an AUR helper like paru.
```bash
paru -S dotnet-runtime-6.0
```
--------------------------------
### Install .NET 6.0 Runtime on Debian/Ubuntu
Source: https://github.com/lavagang/melonwiki/blob/master/docs/gettingstarted.md
Install the .NET 6.0 Runtime required for Il2Cpp-based games on Debian or Ubuntu systems using the apt package manager.
```bash
sudo apt update && sudo apt install dotnet-runtime-6.0
```
--------------------------------
### Install .NET 6.0 Runtime on Fedora
Source: https://github.com/lavagang/melonwiki/blob/master/docs/gettingstarted.md
Install the .NET 6.0 Runtime required for Il2Cpp-based games on Fedora systems using the dnf package manager.
```bash
sudo dnf install dotnet-runtime-6.0
```
--------------------------------
### Example Class for Patching
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/patching.md
This class serves as a reference for demonstrating patching techniques. It includes static fields, properties, and private methods that can be targeted for patching.
```csharp
public class Example
{
public static Example Instance;
internal string _myField;
private static string _myString { get; set; }
private static void PrivateMethod(string param1)
{
}
private static void PrivateMethod(int param1)
{
}
}
```
--------------------------------
### Manual Harmony Patching at Runtime
Source: https://context7.com/lavagang/melonwiki/llms.txt
Provides runtime flexibility for patching methods using HarmonyX. This example demonstrates resolving the target method and prefix method dynamically.
```cs
using HarmonyLib;
using System.Reflection;
public class MyMod : MelonMod
{
public override void OnInitializeMelon()
{
// Resolve the target method at runtime
MethodInfo target = typeof(Example)
.GetMethod("PrivateMethod", new Type[] { typeof(int) });
MethodInfo prefixMethod = typeof(MyPatchHelpers)
.GetMethod(nameof(MyPatchHelpers.MyPrefix),
BindingFlags.Static | BindingFlags.NonPublic);
// null = no postfix; HarmonyInstance auto-unpatches on Melon deinitialization
HarmonyInstance.Patch(target, new HarmonyMethod(prefixMethod), null);
}
}
internal static class MyPatchHelpers
{
private static void MyPrefix()
{
MelonLogger.Msg("Prefix running!");
}
}
```
--------------------------------
### Define Example Class for Reflection
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/reflection.md
This class serves as a target for reflection examples, containing public, internal, and private members.
```csharp
public class Example
{
public static Example Instance;
internal string _myField;
private static string _myString { get; set; }
private static void PrivateMethod(string param1)
{
}
}
```
--------------------------------
### Basic Time Freezer Mod Example
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/quickstart.md
This mod allows the user to freeze and unfreeze the game by pressing the spacebar. It references UnityEngine.CoreModule, UnityEngine.InputLegacyModule, and UnityEngine.IMGUIModule assemblies.
```csharp
using UnityEngine;
using MelonLoader;
[assembly: MelonInfo(typeof(TimeFreezer.TimeFreezerMod), "Time Freezer", "1.0.0", "SlidyDev")]
namespace TimeFreezer
{
public class TimeFreezerMod : MelonMod
{
private static KeyCode freezeToggleKey;
private static bool frozen;
private static float baseTimeScale;
public override void OnEarlyInitializeMelon()
{
freezeToggleKey = KeyCode.Space;
}
public override void OnLateUpdate()
{
if (Input.GetKeyDown(freezeToggleKey))
{
ToggleFreeze();
}
}
public static void DrawFrozenText()
{
GUI.Label(new Rect(20, 20, 1000, 200), "Frozen");
}
private static void ToggleFreeze()
{
frozen = !frozen;
if (frozen)
{
Melon.Logger.Msg("Freezing");
MelonEvents.OnGUI.Subscribe(DrawFrozenText, 100); // Register the 'Frozen' label
baseTimeScale = Time.timeScale; // Save the original time scale before freezing
Time.timeScale = 0;
}
else
{
Melon.Logger.Msg("Unfreezing");
MelonEvents.OnGUI.Unsubscribe(DrawFrozenText); // Unregister the 'Frozen' label
Time.timeScale = baseTimeScale; // Reset the time scale to what it was before we froze the time
}
}
public override void OnDeinitializeMelon()
{
if (frozen)
{
ToggleFreeze(); // Unfreeze the game in case the melon gets unregistered
}
}
}
}
```
--------------------------------
### MelonPlugin - Early-Loading Plugin Setup
Source: https://context7.com/lavagang/melonwiki/llms.txt
Provides the basic structure for a MelonPlugin, which loads before mods and has access to early-lifecycle callbacks. Requires Harmony patching and specific assembly attributes.
```csharp
using System;
using MelonLoader;
[assembly: MelonInfo(typeof(MyPlugin.BootstrapPlugin), "Bootstrap Plugin", "1.0.0", "AuthorName")]
[assembly: MelonGame] // universal — any game
[assembly: HarmonyDontPatchAll] // required for plugins
namespace MyPlugin
{
public class BootstrapPlugin : MelonPlugin
{
// Called as soon as all Plugins from the Plugins folder are initialized
public override void OnPreInitialization()
{
MelonLogger.Msg("Plugin: pre-initialization");
}
// Called before any Mods are loaded
public override void OnPreModsLoaded()
{
MelonLogger.Msg("Plugin: about to load mods");
}
// Called after all MelonLoader components are fully initialized
public override void OnApplicationStarted()
{
MelonLogger.Msg("Plugin: everything initialized");
}
public override void OnInitializeMelon()
{
// Manual Harmony patching required for plugins
HarmonyInstance.PatchAll(MelonAssembly.Assembly);
}
}
}
```
--------------------------------
### Hooking a Native C Function with MelonLoader
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/patching.md
This example demonstrates hooking a native C function in a DLL. Ensure the delegate signature matches the target function and correctly calculate the function's offset within the DLL.
```cs
// Required to get base address of the DLL.
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
// Define the delegate matching our Dll's function parameters.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private unsafe delegate IntPtr LoadBufferDelegate(IntPtr luaState, IntPtr buffer, IntPtr size);
private static LoadBufferDelegate patch;
private static NativeHook Hook;
public static unsafe IntPtr lua_loadbuffer(IntPtr luaState, IntPtr buffer, IntPtr size)
{
MelonLogger.Msg($"Hooked in! bufferSize: {size.ToInt64()}");
// Converting buffer in to C# byte array
int length = (int)size.ToInt64();
byte[] managedArray = new byte[length];
Marshal.Copy(buffer, managedArray, 0, length);
// Do some cool stuff
// Execute the original function
var hookTrampoline = Hook.Trampoline(luaState, buffer, size);
return hookTrampoline;
}
public override unsafe void OnLateInitializeMelon()
{
// Function's offset
var functionOffset = 0x163D30;
// Getting DLL's base address, and adding function's offset
var targetAddress = GetModuleHandle("tolua.dll") + functionOffset;
// Rest same as before, getting delegate's pointer and creating the hook.
patch = lua_loadbuffer;
IntPtr delegatePointer = Marshal.GetFunctionPointerForDelegate(patch);
Hook = new NativeHook (targetAddress, delegatePointer);
Hook.Attach();
}
```
--------------------------------
### Implementing Interfaces with Il2Cpp
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/il2cppdifferences.md
Example of implementing multiple interfaces (`ISaveData`, `ISerializable`) on a custom Il2Cpp class using `RegisterTypeInIl2CppWithInterfaces`. The class inherits from `Il2CppSystem.Object` and implements interface methods.
```csharp
using IL2cppExample.Saving; // This is the namespace of the imaganary interfaces
[RegisterTypeInIl2CppWithInterfaces(typeof(ISaveData), typeof(ISerializable))]
[Serializable]
public class MyData : Il2CppSystem.Object
{
public MyData(IntPtr ptr) : base(ptr) { }
// Implement the interface methods here
public void Save() { /* ... */ }
public void Load() { /* ... */ }
}
```
--------------------------------
### Manual Harmony Patching Example
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/patching.md
Use this code to manually patch a method with a prefix and finalizer, leaving the postfix null. Ensure your prefix and finalizer methods are static. The Harmony instance name differs based on your MelonLoader version.
```csharp
using HarmonyLib;
// In a method, preferabally in OnInitializeMelon
HarmonyInstance harmony = this.HarmonyInstance;
MethodInfo privateMethod = typeof(Example).GetMethod("PrivateMethod", new Type [] { typeof(int) });
MethodInfo myPrefix = ;// Get the prefix here
MethodInfo myFinalizer = ;// Get the finalizer here
// Prefix and finalizer have values, however the postfix does not.
harmony.Patch(privateMethod, myPrefix, null, myFinalizer);
```
--------------------------------
### Starting and Stopping Coroutines with MelonCoroutines
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/il2cppdifferences.md
Use MelonCoroutines.Start to begin a coroutine and MelonCoroutines.Stop to end it. The coroutine runs at the end of the frame.
```csharp
System.Collections.IEnumerator myCoroutine() {
LoggerInstance.Msg("This logs immediately");
yield return null;
LoggerInstance.Msg("This logs on the next frame");
yield return new WaitForSeconds(5.0);
LoggerInstance.Msg("This logs after 5 seconds of the last log");
}
// ... in some method
object routine = MelonCoroutines.Start(myCoroutine());
// If you ever decide to stop the routine, pass the return value of
// Start into Stop
MelonCoroutines.Stop(routine);
```
--------------------------------
### Basic Il2Cpp Type Inheritance
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/il2cppdifferences.md
A fundamental example of a custom component inheriting from MonoBehaviour and registering with Il2Cpp. Includes constructors for both Il2Cpp-side and Mono-side instantiation.
```csharp
// You must reference `Il2CppInterop.Runtime.dll` for this to work
using Il2CppInterop.Runtime;
[RegisterTypeInIl2Cpp]
class MyCustomComponent : MonoBehaviour
{
public MyCustomComponent(IntPtr ptr) : base(ptr) {}
// Optional, only used in case you want to instantiate this class in the mono-side
// Don't use this on MonoBehaviours / Components!
public MyCustomComponent() : base(ClassInjector.DerivedConstructorPointer()) => ClassInjector.DerivedConstructorBody(this);
// Code, same as in a normal component
}
```
--------------------------------
### Manual MelonLoader Installation (64-bit/32-bit)
Source: https://context7.com/lavagang/melonwiki/llms.txt
Download and extract MelonLoader manually for 64-bit or 32-bit games. This method places the MelonLoader folder and version.dll directly into the game's root directory.
```sh
# 64-bit game
wget https://github.com/LavaGang/MelonLoader/releases/latest/download/MelonLoader.x64.zip
unzip MelonLoader.x64.zip -d /path/to/GameFolder/
```
```sh
# 32-bit game
wget https://github.com/LavaGang/MelonLoader/releases/latest/download/MelonLoader.x86.zip
unzip MelonLoader.x86.zip -d /path/to/GameFolder/
# Both steps extract: MelonLoader/ folder + version.dll into the game's root directory
```
--------------------------------
### Find Method Containing a Specific String Literal
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/xrefscanning.md
Finds a method within the Example class that contains the string literal "Example Xref". It scans each method's cross-references to check for the global string.
```csharp
MethodInfo fooMethod = typeof(Example).GetMethods() // Get the methods in the Example class
.First(mi => XrefScanner.XrefScan(mi) // Scan each method
.Any(instance => instance.Type == XrefType.Global && instance.ReadAsObject() != null && instance.ReadAsObject().ToString() == "Example Xref")); // Determine if the method has the literal
```
--------------------------------
### Get a Private Static Method
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/reflection.md
Retrieve a private static method using its name and binding flags. Ensure you import System.Reflection.
```csharp
using System.Reflection;
// Inside the method body
Type exampleType = typeof(Example);
MethodInfo privateMethod = exampleType.GetMethod("PrivateMethod");
```
--------------------------------
### Set DOTNET_ROOT for Sandboxed Environments
Source: https://github.com/lavagang/melonwiki/blob/master/docs/gettingstarted.md
When using sandboxed environments like Flatpak or Snap, set the DOTNET_ROOT environment variable to the path of your .NET installation to resolve 'Failed to load Hostfxr' errors.
```bash
DOTNET_ROOT="/path/to/your/dotnet" ./game_binary
```
--------------------------------
### Debugging Mono Games with dnSpy
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/debugging.md
Use dnSpy to attach to a running Mono game for debugging. Ensure dnSpy is installed and follow the steps to configure the debugger and add breakpoints.
```bash
--melonloader.debug
```
--------------------------------
### IL2CPP Type Operations
Source: https://context7.com/lavagang/melonwiki/llms.txt
Demonstrates how to get IL2CPP types, cast IL2CPP objects, and perform implicit string casts. Note that C# 'as' or direct casts are not compatible with IL2CPP objects.
```csharp
using Il2CppInterop.Runtime;
// Get the IL2CPP type (not the Mono proxy type)
Resources.FindObjectsOfTypeAll(Il2CppType.Of());
// Cast IL2CPP objects (cannot use C# 'as' or direct cast)
MyChildClass child = parentInstance.TryCast(); // returns null on failure
MyChildClass child2 = parentInstance.Cast(); // throws on failure
// Implicit string cast
Debug.Log((Il2CppSystem.String)"Hello from IL2CPP!");
```
--------------------------------
### Getting Il2Cpp Types with Il2CppType.Of()
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/il2cppdifferences.md
Reference `Il2CppInterop.Runtime.dll`. Use `Il2CppType.Of()` instead of `.GetType()` to get the correct Il2Cpp type for methods.
```csharp
// You must reference `Il2CppInterop.Runtime.dll` for this.
using Il2CppInterop.Runtime;
Resources.FindObjectsOfTypeAll(Il2CppType.Of());
```
--------------------------------
### IL2CPP Events and Actions
Source: https://context7.com/lavagang/melonwiki/llms.txt
Illustrates how to subscribe to IL2CPP events, as standard C# +=/-= operators do not work. It also shows how to use CombineImpl if the 'add_' prefix is stripped by the IL2CPP compiler.
```csharp
// Subscribe to an IL2CPP event (+=/-= don't work on IL2CPP types)
playerManagerInstance.add_onPlayerJoin(
new Il2CppSystem.Action(OnPlayerJoin));
// If add_ is stripped by the IL2CPP compiler, use CombineImpl
playerManagerInstance.onPlayerJoin
.CombineImpl((Il2CppSystem.Action)OnPlayerJoin);
void OnPlayerJoin(Player player)
{
LoggerInstance.Msg($"Player joined: {player.name}");
}
```
--------------------------------
### C# Initializing and Attaching a Native Hook
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/patching.md
This snippet shows the initialization process within a mod's `OnLateInitializeMelon` method. It covers obtaining the target method's pointer using reflection, creating the patch delegate, instantiating the `NativeHook`, attaching it, and storing the hook for trampoline usage. Prefer `OnLateInitializeMelon` to ensure all game systems are loaded.
```csharp
// Our mod's initialize method, prefer OnLateInitializeMelon to make sure everything is loaded and available
public override unsafe void OnLateInitializeMelon()
{
// Getting the IntPtr for our target method with GetIl2CppMethodInfoPointerFieldForGeneratedMethod
IntPtr originalMethod = *(IntPtr*) (IntPtr) Il2CppInteropUtils.
GetIl2CppMethodInfoPointerFieldForGeneratedMethod(typeof(UnityEngine.Object).GetMethod("get_name")).GetValue(null);
// Storing our patch method in one of the delegate fields
_patchDelegate = GetName;
// Getting the IntPtr from _patchDelegate
IntPtr delegatePointer = Marshal.GetFunctionPointerForDelegate(_patchDelegate);
// Creating the NativeHook with our target method' IntPtr and patch delegate' IntPtr
NativeHook hook = new NativeHook (originalMethod, delegatePointer);
// Very important part, actually telling it to attach and hook into the target method
hook.Attach();
// Storing the hook so we can use the trampoline in it to run the original method in our patch
Hook = hook.Trampoline;
}
```
--------------------------------
### Get PropertyInfo for a Property
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/reflection.md
Use `typeof` and `GetProperty` to obtain a `PropertyInfo` object for a property. `BindingFlags` are often not required for public properties.
```csharp
using System.Reflection;
// Inside the method body
Type exampleType = typeof(Example);
PropertyInfo myString = exampleType.GetProperty("_myString");
```
--------------------------------
### Add Prefix and Postfix Methods
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/patching.md
Add static Prefix and Postfix methods to your patch class to execute code before and after the patched method. Prefix runs before, and Postfix runs after.
```csharp
using HarmonyLib;
[HarmonyPatch(typeof(Example), "PrivateMethod", new Type[] { typeof(int) })]
private static class Patch
{
private static void Prefix()
{
// The code inside this method will run before 'PrivateMethod' is executed
}
private static void Postfix()
{
// The code inside this method will run after 'PrivateMethod' has executed
}
}
```
--------------------------------
### Access and Modify Preference Entry Value
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/preferences.md
Demonstrates how to get and set the value of a boolean preference entry. The value is toggled when the 'T' key is pressed.
```csharp
private MelonPreferences_Entry ourFirstEntry;
public override void OnUpdate()
{
if (Input.GetKeyDown(KeyCode.T))
{
bool oldEntryValue = ourFirstEntry.Value;
ourFirstEntry.Value = !oldEntryValue; // Toggles the entry boolean
LoggerInstance.Log($"Our first entry value is {ourFirstEntry.Value}");
}
}
```
--------------------------------
### Create a Basic Melon Plugin
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/MelonPlugins.md
A minimal Melon Plugin must derive from `MelonPlugin` and include the necessary Melon attributes for identification and game targeting.
```csharp
using System;
using MelonLoader;
[MelonInfo(typeof(Test.TestPlugin), "Test", "1.0.0", "SlidyDev")]
[MelonColor(ConsoleColor.Blue)]
[MelonGame("SomeCoolDevTeam", "SomeCoolGame")]
namespace Test
{
public class TestPlugin : MelonPlugin
{
}
}
```
--------------------------------
### Managing Persistent Configuration with MelonPreferences
Source: https://context7.com/lavagang/melonwiki/llms.txt
Shows how to create, load, and manage persistent configuration entries for a mod using MelonPreferences. Configuration is saved to UserData/MelonPreferences.cfg by default.
```cs
using MelonLoader;
using UnityEngine;
public class MyMod : MelonMod
{
private MelonPreferences_Category _config;
private MelonPreferences_Entry _enableFeature;
private MelonPreferences_Entry _multiplier;
public override void OnInitializeMelon()
{
// Create or load the category (idempotent)
_config = MelonPreferences.CreateCategory("MyMod");
// CreateEntry(identifier, defaultValue)
_enableFeature = _config.CreateEntry("EnableFeature", true);
_multiplier = _config.CreateEntry("Multiplier", 1.5f);
// React to value changes
_enableFeature.OnEntryValueChanged.Subscribe((oldVal, newVal) =>
LoggerInstance.Msg($"EnableFeature changed: {oldVal} → {newVal}"));
}
public override void OnUpdate()
{
if (!_enableFeature.Value) return;
if (Input.GetKeyDown(KeyCode.F5))
{
_multiplier.Value += 0.1f;
LoggerInstance.Msg($"Multiplier is now {_multiplier.Value}");
// Saved automatically on quit; force-save manually if needed:
MelonPreferences.Save();
}
}
}
```
--------------------------------
### Dynamically Load and Register Melons from Plugin
Source: https://context7.com/lavagang/melonwiki/llms.txt
Demonstrates how a MelonPlugin can dynamically load and register other Melon assemblies at runtime. This is useful for managing dependencies or loading mods on demand.
```csharp
public override void OnPreModsLoaded()
{
// Load a Melon assembly (returns existing instance if already loaded)
MelonAssembly assembly = MelonAssembly.LoadMelonAssembly("path/to/MyMod.dll");
// assembly.LoadedMelons — successfully resolved Melons
// assembly.RottenMelons — Melons that failed to resolve
// Register all loaded Melons in correct priority/dependency order
MelonBase.RegisterSorted(assembly.LoadedMelons);
// Unregister a specific Melon at runtime
// someMelon.Unregister();
}
```
--------------------------------
### Access Non-Public Members with C# Reflection
Source: https://context7.com/lavagang/melonwiki/llms.txt
Use C# reflection to get, set, or invoke private fields, properties, and methods. Essential for manual patching and analysis.
```cs
using System.Reflection;
// --- Get / Set a non-public instance field ---
FieldInfo field = typeof(Example)
.GetField("_myField", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(exampleInstance, "new value");
string currentValue = (string)field.GetValue(exampleInstance);
// --- Get / Set a private static property ---
PropertyInfo prop = typeof(Example)
.GetProperty("_myString",
BindingFlags.NonPublic | BindingFlags.Static);
prop.SetValue(null, "hello");
string propValue = (string)prop.GetValue(null);
// --- Invoke a private static method (resolving an overload by parameter types) ---
MethodInfo method = typeof(Example)
.GetMethod("PrivateMethod",
BindingFlags.NonPublic | BindingFlags.Static,
null,
new Type[] { typeof(int) },
null);
object returnValue = method.Invoke(null, new object[] { 42 });
```
--------------------------------
### Get an Overloaded Method by Parameter Types
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/reflection.md
Retrieve a specific overloaded method by providing an array of parameter types to GetMethod. This is necessary when multiple methods share the same name.
```csharp
MethodInfo privateMethod = exampleType.GetMethod("PrivateMethod", new Type[] { typeof(int) });
```
```csharp
MethodInfo privateMethod = exampleType.GetMethod("PrivateMethod", new Type[] { typeof(string) });
```
--------------------------------
### Basic MelonMod with Lifecycle Callbacks
Source: https://context7.com/lavagang/melonwiki/llms.txt
Implement the `MelonMod` class to create a basic mod. Utilize lifecycle callbacks like `OnInitializeMelon`, `OnLateUpdate`, and `OnSceneWasLoaded` to hook into the game's events and manage mod functionality.
```csharp
using UnityEngine;
using MelonLoader;
[assembly: MelonInfo(typeof(TimeFreezer.TimeFreezerMod), "Time Freezer", "1.0.0", "SlidyDev")]
[assembly: MelonGame("GameStudio", "GameName")]
namespace TimeFreezer
{
public class TimeFreezerMod : MelonMod
{
private static bool frozen;
private static float baseTimeScale;
// Called before MelonLoader prints mod info — avoid Unity/game references here
public override void OnEarlyInitializeMelon()
{
MelonLogger.Msg("Early init — safe for pre-Unity setup only");
}
// Called after MelonLoader fully initializes — safe for all Unity/game references
public override void OnInitializeMelon()
{
LoggerInstance.Msg("Mod initialized!");
}
// Called after Unity's first Start() messages have fired
public override void OnLateInitializeMelon()
{
LoggerInstance.Msg("Late init — everything is ready");
}
// Called once per frame
public override void OnLateUpdate()
{
if (Input.GetKeyDown(KeyCode.Space))
ToggleFreeze();
}
// Called when a new scene loads
public override void OnSceneWasLoaded(int buildIndex, string sceneName)
{
LoggerInstance.Msg($"Scene '{sceneName}' (index {buildIndex}) loaded");
}
// Called when the active scene is fully initialized
public override void OnSceneWasInitialized(int buildIndex, string sceneName)
{
LoggerInstance.Msg($"Scene '{sceneName}' initialized");
}
// Called before the mod is unregistered / game closes
public override void OnDeinitializeMelon()
{
if (frozen) ToggleFreeze(); // always clean up
}
private static void ToggleFreeze()
{
frozen = !frozen;
if (frozen)
{
Melon.Logger.Msg("Freezing time");
MelonEvents.OnGUI.Subscribe(DrawFrozenLabel, 100);
baseTimeScale = Time.timeScale;
Time.timeScale = 0f;
}
else
{
Melon.Logger.Msg("Unfreezing time");
MelonEvents.OnGUI.Unsubscribe(DrawFrozenLabel);
Time.timeScale = baseTimeScale;
}
}
private static void DrawFrozenLabel()
{
GUI.Label(new Rect(20, 20, 1000, 200),
"Frozen");
}
}
}
```
--------------------------------
### Get FieldInfo for Non-Public Instance Field
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/reflection.md
Use `typeof` and `GetField` with appropriate `BindingFlags` to obtain a `FieldInfo` object for a non-public instance field. Ensure `System.Reflection` is imported.
```csharp
using System.Reflection;
// Inside the method body
Type exampleType = typeof(Example);
FieldInfo myField = exampleType.GetField("_myField", BindingFlags.NonPublic | BindingFlags.Instance);
```
--------------------------------
### Linux Launch Configuration for MelonLoader
Source: https://context7.com/lavagang/melonwiki/llms.txt
Configure launch settings for MelonLoader on Linux, including Wine/Proton, native Linux games (Steam/non-Steam), and sandboxed environments. Also includes commands for setting up .NET runtime and fixing Cpp2IL permissions.
```sh
# Wine/Proton (Windows game via Steam)
WINEDLLOVERRIDES="version=n,b" %command%
```
```sh
# Linux-native game (non-Steam)
LD_LIBRARY_PATH="/full/path/to/game:$LD_LIBRARY_PATH" \
LD_PRELOAD="MelonLoader.Bootstrap.so:$LD_PRELOAD" ./game_binary
```
```sh
# Linux-native game (Steam)
LD_LIBRARY_PATH="/full/path/to/game:$LD_LIBRARY_PATH" \
LD_PRELOAD="MelonLoader.Bootstrap.so:$LD_PRELOAD" %command%
```
```sh
# Sandboxed environments (Flatpak / Snap / AppImage) — point to your .NET install
DOTNET_ROOT="/path/to/your/dotnet" ./game_binary
```
```sh
# IL2CPP prerequisite on Debian/Ubuntu
sudo apt update && sudo apt install dotnet-runtime-6.0
```
```sh
# Fix Cpp2IL permissions (MelonLoader < 7.2 on Linux)
chmod +x /path/to/game/MelonLoader/Dependencies/Il2CppAssemblyGenerator/Cpp2IL/Cpp2IL
```
--------------------------------
### Registering Custom Commands in MelonMod
Source: https://context7.com/lavagang/melonwiki/llms.txt
Demonstrates how to register parameterless and typed commands using MelonConsole. Commands can be conditionally registered based on the scene.
```cs
public class MyMod : MelonMod
{
private MelonCommand _spawnCommand;
public override void OnInitializeMelon()
{
// Parameterless command
MelonConsole.RegisterCommand(
new MelonCommand("ping", "Prints pong.", new Action(Ping)));
// Command with typed parameters — any type convertible by System.Convert
_spawnCommand = new MelonCommand(
"spawnhuman",
"Spawns a named human of a given age.",
new Action(SpawnHuman),
new MelonCommand.Parameter("name", typeof(string)),
new MelonCommand.Parameter("age", typeof(int)));
MelonConsole.RegisterCommand(_spawnCommand);
// Usage: spawnhuman "Bob Derp" 80
}
// Scene-conditional registration/unregistration
public override void OnSceneWasInitialized(int buildIndex, string sceneName)
{
if (buildIndex == 1)
MelonConsole.RegisterCommand(_spawnCommand);
else
MelonConsole.UnregisterCommand(_spawnCommand.name);
}
private void Ping() => LoggerInstance.Msg("Pong!");
private void SpawnHuman(string name, int age)
=> LoggerInstance.Msg($"Spawned {name}, age {age}.");
}
```
--------------------------------
### Set LD_LIBRARY_PATH and LD_PRELOAD for Steam Launch
Source: https://github.com/lavagang/melonwiki/blob/master/docs/gettingstarted.md
Configure Steam launch options to set LD_LIBRARY_PATH and LD_PRELOAD. This ensures MelonLoader can find its necessary files when launching through Steam.
```sh
LD_LIBRARY_PATH="/full/path/to/game/directory:$LD_LIBRARY_PATH" LD_PRELOAD="MelonLoader.Bootstrap.so:$LD_PRELOAD" %command%
```
--------------------------------
### Set LD_LIBRARY_PATH and LD_PRELOAD for Non-Steam Launch
Source: https://github.com/lavagang/melonwiki/blob/master/docs/gettingstarted.md
Use this command to set the LD_LIBRARY_PATH and LD_PRELOAD environment variables for non-Steam games. Ensure the path points to your game directory and the library filename is correct.
```sh
LD_LIBRARY_PATH="/full/path/to/game/directory:$LD_LIBRARY_PATH" LD_PRELOAD="MelonLoader.Bootstrap.so:$LD_PRELOAD" ./game_binary
```
--------------------------------
### Debugging Mono Games with dnSpy
Source: https://context7.com/lavagang/melonwiki/llms.txt
Instructions for setting up the dnSpy debugger for Mono games. Ensure the game is launched with the '--melonloader.debug' argument.
```text
1. Open dnSpy → Debug → Start Debugging → Engine: Unity
2. Executable: select the game's .exe
3. Arguments: --melonloader.debug
4. Drag your mod DLL into Assembly Explorer and set breakpoints normally
```
--------------------------------
### Get Value of a Property via Reflection
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/reflection.md
Use the `GetValue` method on a `PropertyInfo` object to retrieve the value of a property. The returned `object` will likely need to be cast to the property's type.
```csharp
string myStringValue = (string)myString.GetValue(Example.Instance);
```
--------------------------------
### Register Melon Command with Parameters
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/MelonConsoleModding.md
Register commands that accept arguments. Define parameters using `MelonCommand.Parameter` specifying name and type. The `Action` delegate must match the defined parameters.
```csharp
public override void OnInitializeMelon()
{
var spawnHumanCommand = new MelonCommand("spawnhuman", "Spawns a new human.", new Action(SpawnHuman), new MelonCommand.Parameter("name", typeof(string)), new MelonCommand.Parameter("age", typeof(int)));
MelonConsole.RegisterCommand(spawnHumanCommand);
}
private void SpawnHuman(string name, int age)
{
LoggerInstance.Msg($"Spawned {name}! {name} is {age} years old.");
}
```
--------------------------------
### Get Value of a Field via Reflection
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/reflection.md
Use the `GetValue` method on a `FieldInfo` object to retrieve the value of a field. The returned `object` will likely need to be cast to the field's type.
```csharp
string myFieldValue = (string)myField.GetValue(null);
```
--------------------------------
### Manual Il2Cpp Class Registration
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/il2cppdifferences.md
Demonstrates manually registering a custom Il2Cpp type using `ClassInjector.RegisterTypeInIl2Cpp()` before its first use. Shows adding the component to a GameObject and instantiating it.
```csharp
class MyMod : MelonMod
{
public override void OnApplicationStart()
{
ClassInjector.RegisterTypeInIl2Cpp();
// ...
// And then, add it to a component:
ourGameObject.AddComponent();
// Or in case you want to instantiate it:
new MyCustomComponent(); // Requires the default constructor shown above
}
}
```
--------------------------------
### Subscribe to MelonEvents for GUI
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/quickstart.md
Subscribe to MelonEvents like OnGUI to draw GUI elements. Use a priority value to control execution order; higher values run later.
```csharp
public class MyMod : MelonMod
{
public override void OnInitializeMelon()
{
MelonEvents.OnGUI.Subscribe(DrawMenu, 100); // The higher the value, the lower the priority.
}
private void DrawMenu()
{
GUI.Box(new Rect(0, 0, 300, 500), "My Menu");
}
}
```
--------------------------------
### Specify Platform and Domain Compatibility
Source: https://context7.com/lavagang/melonwiki/llms.txt
Use `MelonPlatform` and `MelonPlatformDomain` to specify the compatible operating systems and runtime domains for your mod. These attributes are available from MelonLoader version 0.4.0 onwards.
```csharp
[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)]
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.IL2CPP)]
```
--------------------------------
### MelonLoader Launch Arguments
Source: https://context7.com/lavagang/melonwiki/llms.txt
Command-line arguments to control MelonLoader's behavior during game launch. Options include disabling mods, enabling debug modes, attaching a debugger, and customizing console output.
```text
--no-mods | Launches without loading any Plugins or Mods
--melonloader.debug | Enable debug mode
--melonloader.launchdebugger | Attach IDE debugger (IL2CPP, ML ≥ 0.6.0)
--melonloader.consolemode | Change console theme display mode (default: 0)
--melonloader.hideconsole | Hide the MelonLoader console window
--melonloader.hidewarnings | Suppress warnings in console output
--melonloader.maxlogs | Max log files kept (default: 10; 0 = unlimited)
--melonloader.loadmodemods | Load mode for Mods (default: 0)
--melonloader.loadmodeplugins | Load mode for Plugins (default: 0)
--melonloader.disablestartscreen | Disable the MelonLoader start screen
--quitfix | Fix hanging process on game close
```
--------------------------------
### Create a Native Hook for IL2CPP Methods
Source: https://context7.com/lavagang/melonwiki/llms.txt
Use this pattern to intercept and modify the behavior of IL2CPP methods when Harmony patching is not feasible. Requires `Il2CppInterop.Runtime`.
```cs
using Il2CppInterop.Runtime;
using System.Runtime.InteropServices;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr GetNameDelegate(IntPtr instance, IntPtr methodInfo);
private static NativeHook _hook;
private static GetNameDelegate _patchDelegate;
public static unsafe IntPtr GetName(IntPtr instance, IntPtr methodInfo)
{
// Call the original method via the trampoline
IntPtr result = _hook.Trampoline(instance, methodInfo);
string originalName = IL2CPP.PointerToValueGeneric(result, false, false);
Melon.Logger.Msg($"Original name: {originalName}");
// Return a replacement string
return IL2CPP.ManagedStringToIl2Cpp("PatchedName");
}
public override unsafe void OnLateInitializeMelon()
{
IntPtr originalMethod = *(IntPtr*)(IntPtr)
Il2CppInteropUtils
.GetIl2CppMethodInfoPointerFieldForGeneratedMethod(
typeof(UnityEngine.Object).GetMethod("get_name"))
.GetValue(null);
_patchDelegate = GetName;
IntPtr patchPtr = Marshal.GetFunctionPointerForDelegate(_patchDelegate);
_hook = new NativeHook(originalMethod, patchPtr);
_hook.Attach();
}
```
--------------------------------
### Debugging IL2CPP Games with IDE Debugger
Source: https://context7.com/lavagang/melonwiki/llms.txt
Launches the game with the debugger enabled for IL2CPP games. Attach your IDE after the game prompts you.
```sh
# Launch game from command prompt; attach your IDE when the pop-up appears
GameName.exe --melonloader.launchdebugger --melonloader.debug
```
--------------------------------
### Scan for Method Calls using XrefScanner
Source: https://context7.com/lavagang/melonwiki/llms.txt
Identify methods by what they call, which is crucial for obfuscated or frequently updated IL2CPP games. Requires `Il2CppInterop.Common.dll`.
```cs
using Il2CppInterop.Common.XrefScans;
var instances = XrefScanner.XrefScan(targetMethodBase);
foreach (XrefInstance instance in instances)
{
MethodBase calledMethod = instance.TryResolve(); // may return null
if (calledMethod != null)
MelonLogger.Msg($"Calls: {calledMethod.Name}");
}
```
--------------------------------
### Declare Optional and Incompatible Dependencies
Source: https://context7.com/lavagang/melonwiki/llms.txt
Use `MelonOptionalDependencies` to list mods that are not strictly required but are recommended. Use `MelonIncompatibleAssemblies` to list mods that should not be run alongside this one.
```csharp
[assembly: MelonOptionalDependencies("SomeMod", "AnotherMod")]
[assembly: MelonIncompatibleAssemblies("ConflictingMod")]
```
--------------------------------
### Register Parameterless Melon Command
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/MelonConsoleModding.md
Use this to register a command that does not require any arguments. Ensure the `Action` delegate matches the command's signature.
```csharp
public override void OnInitializeMelon()
{
var testCommand = new MelonCommand("test", "Just a simple test.", new Action(TestCommand));
MelonConsole.RegisterCommand(testCommand);
}
private void TestCommand()
{
LoggerInstance.Msg("Testing!");
}
```
--------------------------------
### Adding Event Listeners in Il2Cpp
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/il2cppdifferences.md
Demonstrates the correct syntax for adding an event listener to an Il2Cpp type using the generated add_ method. Ensure the listener method signature matches the event's delegate.
```csharp
playerManagerInstance.add_onPlayerJoin(new Action(MyEventListener));
// ...
void MyEventListener(Player player) { /* Do things */ }
```
--------------------------------
### Debugging Il2Cpp Games with an IDE
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/debugging.md
Launch Il2Cpp games with debugger arguments to attach an external IDE. This method requires MelonLoader version 0.6.0 or above.
```bash
--melonloader.launchdebugger --melonloader.debug
```
--------------------------------
### Manual Harmony Patching for Plugins
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/MelonPlugins.md
To enable manual Harmony patching in plugins, add `HarmonyDontPatchAll` to your assembly and explicitly call `HarmonyInstance.PatchAll` within your `OnInitializeMelon` override.
```csharp
[assembly: HarmonyDontPatchAll]
// In your MelonPlugin class:
public override void OnInitializeMelon()
{
HarmonyInstance.PatchAll(MelonAssembly.Assembly);
}
```
--------------------------------
### Make Cpp2IL Executable
Source: https://github.com/lavagang/melonwiki/blob/master/docs/gettingstarted.md
Manually set execute permissions for the Cpp2IL binary if you encounter issues with the Assembly Generator not running. This is typically needed for older versions of MelonLoader.
```sh
chmod +x /path/to/game/folder/MelonLoader/Dependencies/Il2CppAssemblyGenerator/Cpp2IL/Cpp2IL
```
--------------------------------
### Create and Initialize Melon Preference Entry
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/preferences.md
Creates a boolean preference entry within a category during the Melon initialization phase. The entry is initialized with a default value.
```csharp
private MelonPreferences_Category ourFirstCategory;
private MelonPreferences_Entry ourFirstEntry;
public override void OnInitializeMelon()
{
ourFirstCategory = MelonPreferences.CreateCategory("OurFirstCategory");
ourFirstEntry = ourFirstCategory.CreateEntry("OurFirstEntry", true);
}
```
--------------------------------
### Il2Cpp Event Handling
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/il2cppdifferences.md
Shows how to add and remove event listeners in Il2Cpp environments using generated methods. Use these when standard += and -= operators are not available.
```csharp
Action onPlayerJoin;
public void add_onPlayerJoin(Il2CppSystem.Action value) { /* does stuff with onPlayerJoin */ }
public void remove_onPlayerJoin(Il2CppSystem.Action value) { /* does stuff with onPlayerJoin */ }
```
--------------------------------
### Reading Exceptions Without Line Numbers in C#
Source: https://context7.com/lavagang/melonwiki/llms.txt
Explains how to use the IL offset from exception logs to find the exact source line in dnSpy for debugging.
```cs
// Example log output:
// System.Exception: An exception was thrown
// at MyMod.MainClass.OnUpdate () [0x0001a] in :0
// The hex offset [0x0001a] is the IL offset.
// In dnSpy: right-click the erroring method → Edit IL Instructions...
// Match the offset to identify the exact source line.
```
--------------------------------
### Verbose Logging Command for Android
Source: https://github.com/lavagang/melonwiki/blob/master/docs/android/mod_development.md
This command provides more detailed logging output for MelonLoader on Android, useful for in-depth debugging. It includes additional components compared to the normal command.
```bash
adb logcat -v time MelonLoader:D CRASH:D Mono:D mono:D mono-rt:D Zygote:D A64_HOOK:V DEBUG:D funchook:D Unity:D Binder:D AndroidRuntime:D *:S
```
--------------------------------
### Implement Melon Callbacks
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/quickstart.md
Override virtual callbacks to hook into game events like scene loading. Ensure you use the correct callback for your needs, as some run before full initialization.
```csharp
using MelonLoader;
namespace MyProject
{
public class MyMod : MelonMod
{
public override void OnSceneWasLoaded(int buildIndex, string sceneName)
{
LoggerInstance.Msg($"Scene {sceneName} with build index {buildIndex} has been loaded!");
}
}
}
```
--------------------------------
### Register Custom MonoBehaviour Components in IL2CPP
Source: https://context7.com/lavagang/melonwiki/llms.txt
Create and register custom MonoBehaviour components for use in IL2CPP games. Use the `[RegisterTypeInIl2Cpp]` attribute or `ClassInjector.RegisterTypeInIl2Cpp`.
```cs
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
[RegisterTypeInIl2Cpp]
public class MyCustomComponent : MonoBehaviour
{
// Required: IntPtr constructor for the IL2CPP side
public MyCustomComponent(IntPtr ptr) : base(ptr) {}
// Optional: default constructor for Mono-side instantiation (NOT for MonoBehaviours)
public MyCustomComponent()
: base(ClassInjector.DerivedConstructorPointer())
=> ClassInjector.DerivedConstructorBody(this);
private void Update()
{
// Works exactly like a normal Unity component
}
}
// In your mod's OnInitializeMelon (if not using the attribute):
ClassInjector.RegisterTypeInIl2Cpp();
someGameObject.AddComponent();
```
--------------------------------
### Logging Messages with LoggerInstance and Melon.Logger
Source: https://context7.com/lavagang/melonwiki/llms.txt
Demonstrates direct access to LoggerInstance within MelonMod methods and static access via Melon.Logger from any class.
```cs
public class MyMod : MelonMod
{
public override void OnInitializeMelon()
{
// Direct instance access (inside MelonMod methods)
LoggerInstance.Msg("Informational message");
LoggerInstance.Warning("Something might be wrong");
LoggerInstance.Error("Something went wrong");
// Call a static helper
HelloWorld();
}
public static void HelloWorld()
{
// Static access via generic singleton — works from any class or static method
Melon.Logger.Msg("Hello from a static context!");
}
}
```
--------------------------------
### Set Mod Load Order with [MelonPriority]
Source: https://context7.com/lavagang/melonwiki/llms.txt
Control the loading order of your mod relative to others. Lower integer values indicate higher priority, meaning the mod will load earlier. The default priority is 0.
```cs
// Lower integer = higher priority; default is 0
[assembly: MelonPriority(-100)] // loads before most mods
[assembly: MelonPriority(100)] // loads after most mods
```
--------------------------------
### Scan Methods That Use a Specific Method
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/xrefscanning.md
Use `XrefScanner.UsedBy()` to find all methods that call a given `MethodBase`. Ensure `Il2CppInterop.Common.dll` is referenced. The `TryResolve()` method on the returned `XrefInstance` can retrieve the `MethodBase` of the calling method, but it may return null.
```csharp
var instances = Il2CppInterop.Common.XrefScans.XrefScanner.UsedBy(methodBase);
foreach (Il2CppInterop.Common.XrefScans.XrefInstance instance in instances)
{
MethodBase calledMethod = instance.TryResolve();
// The rest of your code using this information
}
```
--------------------------------
### Verify Minimum MelonLoader Version
Source: https://context7.com/lavagang/melonwiki/llms.txt
Use `VerifyLoaderVersion` to enforce a minimum required version of MelonLoader for your mod to run. This ensures compatibility with features or APIs introduced in specific versions.
```csharp
// Require MelonLoader 0.6.0 or newer
[assembly: VerifyLoaderVersion(0, 6, 0, true)]
```
--------------------------------
### C# Native Hook for Method Patching
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/patching.md
This snippet demonstrates how to define a delegate for a native hook, set up static fields for the hook and delegate, and implement the patch method. Ensure the delegate signature matches the target method's parameters and return type.
```csharp
// Delegate for our patch, same number of parameters as our patch method
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate IntPtr GetNameDelegate(
IntPtr instance,
IntPtr methodInfo
);
// Two static fields with our delegate type
private static NativeHook Hook;
private static GetNameDelegate _patchDelegate;
// The patch method, dealing with unmanaged to managed then back to unmanaged, so pointers galore
public static unsafe IntPtr GetName(IntPtr instance, IntPtr methodInfo)
{
IntPtr result = hook.Trampoline(instance, methodInfo);
string name = IL2CPP.PointerToValueGeneric (result, false, false);
Melon.Logger.Msg(name);
return IL2CPP.ManagedStringToIl2Cpp("MelonLoader");
}
```
--------------------------------
### Exposing Fields to Il2Cpp
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/il2cppdifferences.md
Shows how to expose an integer field to the Il2Cpp side using `Il2CppValueField`. Note that direct assignment to the field throws an error; use the `.Value` property for modification.
```csharp
using using Il2CppInterop.Runtime.InteropTypes.Fields;
[RegisterTypeInIl2Cpp]
class MyCustomComponent(IntPtr ptr) : MonoBehaviour(ptr)
{
// This field will be visible to IL2CPP
public Il2CppValueField Test;
}
// Usage in C# side:
```cs
MyCustomComponent component = GetComponent();
component.Test = 42 // This will throw an error
component.Test.Value = 42; // This will work
```
```
--------------------------------
### Normal Logging Command for Android
Source: https://github.com/lavagang/melonwiki/blob/master/docs/android/mod_development.md
Use this command to view standard MelonLoader logs on Android via ADB. It filters logs for specific components.
```bash
adb logcat -v time MelonLoader:D CRASH:D Mono:W mono:D mono-rt:D Zygote:D A64_HOOK:V DEBUG:D Binder:D AndroidRuntime:D *:S
```
--------------------------------
### Invoke a Method Using Reflection
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/reflection.md
Call a method obtained via reflection. Pass null for static methods and an object array for parameters. The return value is an object.
```csharp
privateMethod.Invoke(null, new object[] { "param value" });
```
--------------------------------
### MelonPlatformDomain Assembly Attribute
Source: https://github.com/lavagang/melonwiki/blob/master/docs/modders/attributes.md
Optional attribute to specify the compatible domain (e.g., IL2CPP) for your mod using the CompatibleDomains enum.
```csharp
using MelonLoader;
// ...
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.IL2CPP)]
```