-
Notifications
You must be signed in to change notification settings - Fork 12
Creating Mods
If you have some level of familiarity with C#, getting started making mods should not be too difficult. All that is needed is the .NET 9.0 SDK and some form of IDE is strongly recommended. This guide assumes you already have Resonite and ResoniteModLoader installed.
Grab the .NET 9.0 SDK from here https://dotnet.microsoft.com/en-us/download, this is required for compiling your mod.
- Visual Studio
- Visual Studio Code
- Rider
- Technically you can use any text editor as well but will need to rely much more on extensions for features and a terminal for building.
- Make a new .NET class library against
.NET 9.0. - Add
ResoniteModLoader.dllas a reference and optionally HarmonyLib (0Harmony.dll) - Add references to Resonite libraries as needed from the Resonite install folder
C:\Program Files (x86)\Steam\steamapps\common\Resonite\
Most likely will want to add references to FrooxEngine and Elements.Core
<Reference Include="FrooxEngine">
<HintPath>$(ResonitePath)FrooxEngine.dll</HintPath>
<Private>False</Private>
</Reference>Alternatively download or fork the ExampleMod project to serve as a starting point.
You'll likely want to grab a C# de-compiler to explore the code within the game. Here are a few popular options:
Called once per mod during FrooxEngine initialization. This is where you will likely want to 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 and settings will only be saved if needed.
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.
If you rely on another mod or some of the other FrooxEngine systems to be done initializing before doing something, move the dependent code to load later after all the mods have had a chance to start. This can be 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();
});Runs just after RunPostInit when the engine reports it is ready ℹ️6:48:41 AM.716 (FPS: 0): Engine Ready!
Engine.Current.OnReady += () => {
Msg("OnReady");
};Game is shutting down, called after mod loader configs are saved. This is where you should cleanly shutdown and dispose created systems.
Engine.Current.OnShutdown += () => {
Msg("OnShutdown");
};You can explore Engine.Current and Engine.Current.WorldManager for a decent couple more events.
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.
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
}
//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
}
Msg("A message that shows only when the mod is enabled");
//Do stuff after everything in the original MethodName has run.
}
}
}Alternatively use the ExampleMod repo as a template to start from.
Several logging methods are provided by the mod loader and logs will be placed into Resonite's main Log Files.
Informational messages, should be kept to status and information, if you need to alert the user of something use Warn or Error logs instead.
Msg("A regular log entry")Warning messages for potential issues that aren't necessarily errors or preventing the game from continuing.
Warn("A warning entry")Error messages for something that may cause breakage or lack of function entirely.
Error("an error log");When running the modloader in debug mode, Debug("Message") can also be used for more verbose messages to assist in diagnosing issues in a mod. These will be ignored and not write to the log otherwise
All log types will create messages that look similar to this one for easy identification within the log file
3:14:42 AM.418 (144 FPS) [INFO] [ResoniteModLoader/ExampleMod] A regular log entry
Add the following into your .csproj file.
<ItemGroup>
<Using Remove="System.Net.Http" />
</ItemGroup>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.
When using the ExampleMod Project, you may get an error The reference is invalid or unsupported when trying to add a reference to ResoniteModLoader, Harmony, or FrooxEngine. This occurs because the your project already has references setup for these files, if you are using the ExampleMod, you'll need to update those in the csproj.

To resolve this, edit your .csproj file and add the path where your game is installed as one of the paths for ResonitePath. This will be added after the other conditional paths defined in ExampleMod.csproj. Here is an example path where Resonite is installed in a SteamLibrary on another drive.
<ResonitePath Condition="Exists('R:\SteamLibrary\steamapps\common\Resonite\')">R:\SteamLibrary\steamapps\common\Resonite\</ResonitePath>