Skip to content

Creating Mods

Delta edited this page Aug 27, 2024 · 3 revisions

If you have some level of familiarity with C#, getting started making mods should not be too difficult.

Basic Visual Studio setup

  1. Make a new .NET library against .NET version 4.7.2.
  2. Add ResoniteModLoader.dll as a reference and optionally HarmonyLib (0harmony.dll)
  3. Remove the reference to System.Net.Http if it was added automatically as it will make the compiler angry
  4. Add references to Resonite libraries as needed from \Resonite\Resonite_Data\Managed

Decompilers

You'll likely want to grab a decompiler if you don't have one already to take a look at existing code. Here are a few popular options:

DnSpyEx, dotPeek, ILSpy

Hooks

OnEngineInit()

Called once per mod during FrooxEngine initialization. This is where you will set up and apply any harmony patches or setup anything your mod will need.

Happens before OnEngineInit()

  • Load Locales
  • Configs
  • Plugin initialization

Happens after OnEngineInit()

  • Input/Head device setup
  • Local DB initialization
  • Networking initialization
  • Audio initialization
  • SkyFrost Interface
  • RunPostInit
  • Worlds loading, including Local home and Userspace

Here is a typical usage:

    public override void OnEngineInit() {
    	//If you don't need any configs, these 2 can be left out
        Config = GetConfiguration(); //Get the current ModConfiguration for this mod.
        Config.Save(true); //If you'd like to save the default config values to file, otherwise you can omit this

        Harmony harmony = new Harmony("com.example.ExampleMod"); //this is your instance of harmony, all your patches should be applied using this.
        //typically a reverse domain name is used here (https://en.wikipedia.org/wiki/Reverse_domain_name_notation)
        harmony.PatchAll(); // Will patch all patches that you've setup with annotations.
    }

Harmony docs on patching using annotations.

RunPostInit

If you rely on another mod or some of the other FrooxEngine systems to be done initializing before doing something, move the dependant code to load later after all the mods have had a chance to start. This is done with Engine.Current.RunPostInit added in your OnEngineInit(), here are 2 examples.

Engine.Current.RunPostInit(FunctionToCall);

OR

Engine.Current.RunPostInit(() => {
    //Code to call after Initialization
    FunctionToCall();
    AnotherFunctionToCall();
});

Mod Configuration

ResoniteModLoader provides a built-in configuration system that can be used to persist configuration values for mods. More information is available on the Config System page.

Example Mod

using HarmonyLib; // HarmonyLib comes included with a ResoniteModLoader install
using ResoniteModLoader;
using System;
using System.Reflection;

namespace ExampleMod;

public class ExampleMod : ResoniteMod {
    public override string Name => "ExampleMod";
    public override string Author => "YourNameHere";
    public override string Version => "1.0.0"; //Version of the mod, should match the AssemblyVersion
    public override string Link => "https://github.com/YourNameHere/ExampleMod"; // Optional link to a repo where this mod would be located

    [AutoRegisterConfigKey]
    private static readonly ModConfigurationKey<bool> enabled = new ModConfigurationKey<bool>("enabled", "Should the mod be enabled", () => true); //Optional config settings

    private static ModConfiguration Config; //If you use config settings, this will be where you interface with them.

    public override void OnEngineInit() {
        Config = GetConfiguration(); //Get the current ModConfiguration for this mod
        Config.Save(true); //If you'd like to save the default config values to file
        Harmony harmony = new Harmony("com.example.ExampleMod"); //typically a reverse domain name is used here (https://en.wikipedia.org/wiki/Reverse_domain_name_notation)
        harmony.PatchAll(); // do whatever LibHarmony patching you need, this will patch all [HarmonyPatch()] instances

        //Various log methods provided by the mod loader, below is an example of how they will look
        //3:14:42 AM.069 ( -1 FPS)  [INFO] [ResoniteModLoader/ExampleMod] a regular log
        Debug("a debug log");
        Msg("a regular log");
        Warn("a warn log");
        Error("an error log");
    }

    //Example of how a HarmonyPatch can be formatted, Note that the following isn't a real patch and will not compile.
    [HarmonyPatch(typeof(ClassNameHere), "MethodNameHere")]
    class ClassNameHere_MethodNameHere_Patch {
        //Postfix() here will be automatically applied as a PostFix Patch
        static void Postfix(ClassName __instance) {
            if(!Config.GetValue(enabled)) {//Use Config.GetValue() to use the ModConfigurationKey defined earlier
                return; //In this example if the mod is not enabled, we'll just return before doing anything
            }
            //Do stuff after everything in the original MethodName has run.
        }
    }
}

Common Errors

Compiler error about System.Net.Http

Add the following into your .csproj file.

<ItemGroup>
    <Using Remove="System.Net.Http" />
</ItemGroup>

C# Feature not available

If you have a compiler error mentioning a specific feature is not available in the current C# language, you will need to update your project to use a newer LangVersion. To do so, edit your project file and set LangVersion to the version specified or later. Refer to the Microsoft documentation for available versions. Add or change the following in your .csproj file. Ensure you only have one entry for this.

<LangVersion>10.0</LangVersion>

Example error:

Error   CS8773  Feature 'file-scoped namespace' is not available in C# 7.3. Please use language version 10.0 or greater.

Additional Resources