Code modding guides

These guides are for mods that ship compiled C# code.

For C# patches to game content, see Patching from C#.

Campaign features in a C# mod

Each campaign uses a feature set of campaign features. Each mod has a default feature, ModId:ModId. This feature is on when the mod is on in that campaign.

A mod that is off in the loaded campaign is still installed:

  • Its assembly loads.

  • The OnPreInitialized, OnInitialized and OnPostInitialized methods of its plugin run as usual.

For a mod that is off, Redux disables these items:

  • patches

  • content assets (see Loading assets)

  • campaign hooks

  • campaign settings

  • app bar buttons

Code that your mod runs directly continues to run. Examples are Harmony patches, message subscriptions and UI that your mod builds directly. Your code must check if the mod is on.

The plugin base classes GeneralMod, MonoBehaviourMod and KerbalMod supply the members for these checks.

Enabled

Enabled is true while the default feature of your mod is on in the active feature set. Check it in all code that your mod runs directly:

private void OnVesselDeltaVChanged(MessageCenterMessage message)
{
    if (!Enabled)
        return;

    // ...
}

The active feature set changes at these times:

  • when the game boots into the main menu

  • when a campaign loads

  • when the player goes back to the main menu

At the main menu, only Redux, Space Warp, Patch Manager and V-SwiFT are on. All other mods are off.

Loading assets

Load the Addressables assets of your mod through the SpaceWarp2.API.Loading API. Do not call Addressables directly, because the feature gate does not see those loads.

  • Loading.AddAddressablesLoadingAction loads a label one time, at boot. Use it for assets that all campaigns use, for example UI and icons. These assets load also when your mod is off. Assets that you keep stay in memory in all campaigns, so keep them small.

  • Loading.AddSetLoadingAction loads a label for each feature set. Its reset clears the data each time the feature set changes. The label is content, so its assets load only while your mod is on. Use it for content data.

  • SaveLoad.AddStepAfter adds a step after a named step of the game load, campaign load or campaign save flow.

Register loading actions in OnPreInitialized at the latest. Register a step before the step that it follows finishes. The game load flow runs one time, so for a step of that flow, register in OnPreInitialized at the latest. If the step that it follows has already finished, the game logs a warning and the step does not run.

Mod features

Put [Feature("Id")] on a bool field of your plugin class. The field then shows if that feature is on. A bare id is a feature of your mod. A qualified id, for example Ksp2Redux:Parts, is a feature of any mod. Redux writes the field each time a feature set activates.

using SpaceWarp2.API.Features;
using SpaceWarp2.API.Mods;

public class MyModPlugin : GeneralMod
{
    [Feature("HardMode")]
    private bool _hardMode;
}

Declare the feature in one of these locations:

  • the features section of your swinfo

  • your SDK Mod asset

  • your code, with Patching.Mod.DefineFeature("HardMode") in OnPreInitialized

Campaign hooks

OnCampaignLoaded() runs each time a campaign that has your mod on finishes loading. This includes quickloads and reverts. OnCampaignUnloading() runs before that campaign unloads.

  • Set up per-campaign state in OnCampaignLoaded().

  • Remove that state in OnCampaignUnloading().

Neither hook runs for a campaign that has your mod off.

public override void OnCampaignLoaded()
{
    if (_hardMode)
    {
        // Set up hard mode for this campaign
    }
}

public override void OnCampaignUnloading()
{
    // Undo what OnCampaignLoaded set up
}

Feature set changes

SpaceWarp2.API.Features.ActiveFeatures reads the active feature set from all code:

  • ActiveFeatures.Enabled gives the ids of the enabled features. It is null before a feature set activates.

  • ActiveFeatures.IsEnabled(id) examines one qualified feature id.

  • ActiveFeatures.IsModEnabled(modId) examines if a mod is on.

  • ActiveFeatures.Changed is an Action<IReadOnlyCollection<string>?> event. It fires after each feature set activates. At that time, all [Feature] fields and Enabled already agree with the new feature set.

public override void OnInitialized()
{
    ActiveFeatures.Changed += enabled => SWLogger.LogInfo($"{SWMetadata.Guid} enabled: {Enabled}");
}

Settings

Put [ConfigFeature("Id")] or [ConfigNoFeature("Id")] next to a [ConfigValue]:

  • [ConfigFeature("Id")] shows the value in the settings menu only while the feature is on.

  • [ConfigNoFeature("Id")] shows the value only while the feature is off.

All values are also hidden while your mod is off. SWMetadata.CampaignConfigFile is the per-campaign config file of your mod. Each campaign keeps its own values in this file.

Patches

C# patches, like Lua patches, need the default feature of your mod. They also accept these conditions:

  • .Feature(...) and .NoFeature(...)

  • [NeedsFeature(...)] and [NoFeature(...)] in the attribute style

See Patching from C#.