### 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)] ```