Skip to content

Mod Integration

Fil edited this page Sep 30, 2023 · 21 revisions

To integrate optional Mod Manager support with your mod.

Banner & Icon

To add a banner for your mod, make sure your mod folder contains a file named "banner.png" containing your mod's banner image.

Likewise, to add an icon for your mod, make sure your mod folder contains a file named "icon.png" containing your mod's icon.

Mod Manifest API

To integrate version checking with the Mod Manager mod, create a new Manifest.xml file in the same directory as your mod's ModInfo.xml. The basic template for a Manifest.xml file looks as so. You can also see the Mod Manager Manifest as another example of how to implement the Mod Manifest file.

<ModManifest>
  <ManifestUrl>https://link-to-remote-mod-folder/Manifest.xml</ManifestUrl> <!-- This is the link to the remote Manifest.xml hosted on another website, for example GitHub.com -->
  <Version>1.0.0</Version> <!-- This is the current mod version to compare the remote mod version against. -->
  <GameVersion ReleaseType="Alpha" Major="20" Minor="5" Build="2" /> <!-- This represents the following: Alpha 20.5 (b2) -->
</ModManifest>

ManifestUrl

When the game is loaded with Mod Manager, all Remote Manifests will be fetched at startup and saved in memory from the specified ManifestUrl from all local Manifest files.

Version

This is also fetched from both the local and remote Manifests and compared during runtime to notify the user if there are any updates for the mod. The versioning system that Mod Manager supports is Semantic Versioning, meaning any changes to the version will notify the user of an update (provided the new number is higher than the previous one).

GameVersion

The GameVersion tag allows the user to infer about compatibility of the mod with the current game version they are running. If the current GameVersion is Alpha 20.5 (b2) but the user is still running a mod supporting the GameVersion Alpha 20.3 (b3), then they are notified that the mod may not be compatible with the current GameVersion. Whereas if they are running a mod with a supported GameVersion of Alpha 19.6 (b8), then they are notified that the mod will in most cases not be compatible with the current GameVersion. This setting does not depend on the ManifestUrl setting, however notifies the user of a new mod updates GameVersion if a Remote Manifest is present and the GameVersion tag is specified in the Remote Manifest.

Mod Manager API

Create a new class in your mod's C# project called ModManagerAPI.cs and paste the code from the original Mod Manager API class definition. This class may periodically update over time so keep your eyes out for patch notes which will mention if your API class needs to be updated.

The Mod Manager API has been built so that it can be seamlessly integrated into your projects without causing any errors during development for both you and your end-users (the players running your mod can still use it without the Mod Manager installed without any errors).

Before you begin using the Mod Manager API wrappers provided in the ModManagerAPI.cs file you have now created, you must always wrap any Mod Manager API function calls in the following if-statements.

if(ModManagerAPI.IsModManagerLoaded())
{
}

Failure to do so will result in warnings being reported to both you and the end-user when running the mod, which will not prevent use of the mod but serve as a warning that Mod Manager API calls will be ignored due to no Mod Manager being detected.

Mod Settings

Mod Settings allow you to store persistent settings for your mod by hooking directly into field values and setting up the settings page that the end-user will see and interact with automatically. Mod Settings only work when the Mod Manager API has detected an instance of the Mod Manager DLL loaded into the current process.

To begin integrating Mod Settings into your mod, create a new instance of the ModManagerAPI.ModSettings wrapper in your mod's IModApi extended class.

using CustomModManager.API;

// Class definition here

public void InitMod(Mod _modInstance)
{
    if(ModManagerAPI.IsModManagerLoaded())
    {
         ModManagerAPI.ModSettings settings = ModManagerAPI.GetModSettings(_modInstance);
    }
}

After compiling your project and opening the game, your Mod Settings Tab will look as such:

Creating a Mod Setting

To create a mod setting with your new ModManagerAPI.ModSettings instance, ensure you have a variable you want to store/load your setting from, then call ModSettings.Hook<T>(string key, string nameUnlocalized, Action<T> setCallback, Func<T> getCallback, Func<T, (string unformatted, string formatted)> toString, Func<string, (T result, bool success)> fromString); with your new ModSettings instance.

// Define your variable at the beginning of the class.
private int test123 = 0; // Note: This value will be the default setting when reset.

modSettings.Hook(
    "test123",                                     // This is the Mod Setting key. This must always be unique for all settings for the mod.
    "xuiModSettingTest123",                        // This is the localization key for the setting's label. The value is fetched from the Localization.txt file in your mod's Config folder.
    value => this.test123 = value,                 // This is the value setter, it updates the value of the variable we have hooked onto.
    () => this.test123,                            // This is the value getter, it gets the value of the variable we have hooked onto.
    toStr => (toStr.ToString(), toStr.ToString()), // This is the string representation of the currently applied setting.
    str =>                                         // This is the converter from the String representation of the setting, back to the variable type of the setting.
{
    bool success = int.TryParse(str, out int val); // Attempt to convert the input to an integer. 
                                                   // If success is true, the input will be accepted.
                                                   // If success is false, the input will be rejected, and the text will change to red to indicate
                                                   // to the user that an invalid input has been entered and will not be saved.
    return (val, success);
});

After compiling your project and opening the game, your setting will appear in the mod's Settings Tab like so:

You will notice that the label shows the localization key. This will automatically get translated when specified in your mod's Localization.txt file.

Mod Setting

Once you've hooked a variable to a mod setting, you can then access the wrapper methods returned from the Hook method.

ModManagerAPI.ModSettings.ModSetting<T> modSetting = settings.Hook(...);

SetAllowedValues

SetAllowedValues turns the text input into a combo selector input with predefined values. Using our previous example, we can limit the inputs to the following numbers 1, 2, 5, 10, 100 like so:

modSetting.SetAllowedValues(new int[] { 1, 2, 5, 10, 100 });

After compiling your project and opening the game, the setting will now look like this:

SetMinimumMaximumAndIncrementValues

SetMinimumMaximumAndIncrementValues calls SetAllowedValues and automatically populates the array with a range of values specified in this method's parameters. For example, consider an array containing a minimum value of 0 and a maximum value of 30, where each element is incremented by 5 from the minimum to the maximum. We can specify this for the setting like so:

modSetting.SetMinimumMaximumAndIncrementValues(0, 30, 5);

This will call the following behind the scenes:

modSetting.SetAllowedValues(new int[] { 0, 5, 10, 15, 20, 25, 30 });

After compiling your project and opening the game, the setting will look like this:

SetWrap

SetWrap allows the combo selector input to wrap around to the initial allowed values.

modSetting.SetWrap(true);

After compiling your project and opening the game, the setting will now work like this:

Design Choices

The API was designed as a wrapper instead of a DLL because one of the features of the API is that the Mod Manager mod should be optional for the end-user, hence it provides an optional way to interface with the Mod Manager mod should the user choose to use it.

The downside to this approach however, is that each implementation of the API will therefore need to be updated every time the main API is updated, hence the Mod Manager runs on Semantic Versioning to try ensure minimal breakage and clear communication between users and Mod Developers. Hence, the API of 1.0.0 may not be compatible with 2.0.0 in some cases, and so on. However the API of 1.0.0 should be compatible with 1.1.0, 1.1.1, etc.