# Campaign features

A campaign feature is a named part of the game content or behavior. Each campaign can enable or disable each feature. Examples are the Redux parts, the Redux tech tree, the rescaled galaxy of a mod, and an optional hard mode of a mod. Each campaign has a **feature set**, which is the list of features that the campaign enables. Patches, assets, prefab patches, Lua hooks, settings and C# behavior each specify the features that they belong to. The game builds each campaign from only the content that its feature set allows.

Features apply to every aspect of modding: patches, assets, settings and code. This page describes the full system:

* what a feature is
* how mods declare features
* how the game selects the feature set of a campaign
* what a feature gates
* the runtime APIs
* how to update an existing mod

For more information about features in Patch Manager, see [Features](/doc/features-geboWdIiDN) under Core Concepts.

## Feature sets

* Each installed mod has a **default feature**. The content of a mod is part of a campaign only when that campaign enables the default feature of the mod.
* A mod can declare more features for optional or alternative content. The mod can gate any of its content on these features.
* The game selects the feature set of a campaign when the player creates the campaign. The feature set comes from the campaign pack and from the choices of the player. The player can change the feature set later.
* A feature set has no order. The content of a campaign is the content that its gates let load, with all enabled patches applied to it.
* Each feature set has its own patch run and its own patch cache. Thus, two campaigns with different feature sets can use the same install, and each campaign gets only its own content.

## Feature ids

A feature id has the form `ModId:FeatureId`, for example `KerbolarUpscale:Rescale` or `Ksp2Redux:Parts`. When a mod writes a bare id, such as `Rescale`, the bare id is qualified with the id of that mod. Id comparison is not case-sensitive.

### The default feature

Each mod has a default feature with the name of the mod, `ModId:ModId`. The mod does not declare it. The default feature means "this mod is enabled in this campaign".

* It requires the default feature of each mod in the `dependencies` list of the `swinfo.json` of the mod.
* It conflicts with the default feature of each mod in the `conflicts` list.
* Each other feature of the mod requires the default feature of the mod. Thus, when any feature of a mod is enabled, the mod is enabled.
* All content that a mod registers or ships needs the default feature of the mod. This applies in addition to the features that the content specifies. The content includes patches, created assets, prefab patches, Addressables content assets, run callbacks, runtime hooks and campaign settings.

Space Warp (`SpaceWarp2`), Patch Manager (`PatchManager`) and V-SwiFT (`VSwift`) are the modding framework. Their default features are enabled in all campaigns. Players never change them.

## Relations between features

A feature can specify four relations. A bare id in a relation refers to a feature of the declaring mod.

| Relation | What it means |
|----------|---------------|
| `requires` | When this feature is enabled, these features are also enabled. Their own requirements are also enabled, and so on. |
| `conflicts` | These features cannot be enabled in the same campaign as this feature. |
| `provides` | This feature replaces these ids when the game resolves requirements. |
| `recommended` | The mod recommends this feature. The **Enable recommended** buttons enable it. |

The game resolves the relations with these rules:

* **Requirements are transitive.** When `KerbolarUpscale:Rescale` is enabled, `KerbolarUpscale:KerbolarUpscale` (the default feature of the mod) and `Ksp2Redux:Galaxy` are also enabled. `Ksp2Redux:Galaxy` requires `Ksp2Redux:Resources`, so `Ksp2Redux:Resources` is also enabled.
* **A conflict applies in both directions.** If one of two features names the other as a conflict, the two features cannot both be enabled.
* **A provider replaces the ids that it provides.** The feature with an id satisfies a requirement on that id. Any feature that provides the id also satisfies the requirement. A mod can provide a feature of any mod. A mod can also provide a virtual id that no mod declares, such as `MyMod:LifeSupport`. A feature cannot provide itself.
* **Providers of one id conflict with each other.** This includes the real feature with that id. Thus, two life support mods that both provide `MyMod:LifeSupport` cannot both be enabled.
* **When a requirement has more than one installed provider, the player selects one.** If only one installed provider can be enabled, the game selects it automatically and records it in the save. Thus, if the player installs a second provider later, that campaign does not change.
* **Recommended is only a hint.** The game does not automatically enable a recommended feature in new campaigns. The feature becomes enabled when the player clicks **Enable recommended** in the mods menu or in the feature editor.
* **The menus do not show features that cannot be satisfied.** If no installed mod can satisfy the requirements of a feature, the menus do not show that feature. For example, this occurs when the mod depends on a mod that is not installed.
* **Two declarations of one id merge.** Their `requires`, `conflicts` and `provides` lists combine. The feature is recommended if one of the two declarations makes it recommended. A mod uses this to add relations to its default feature, in addition to the relations from its dependencies.
* **A mod declares only its own features.** Its relations can name the features of any mod.

## Declaring features

A mod can declare its features in its manifest, in Lua, in C#, or on its SDK Mod asset. All four methods put the features into the same registry. With each method, a mod can add relations to its default feature. To do this, declare the default feature again with the id of the mod.

### In swinfo.json

Add a `features` object. Each key is a bare feature id. Each value lists the relations of that feature. The relation keys are lowercase.

```json
"features": {
    "Rescale": { "requires": ["Ksp2Redux:Galaxy"] },
    "Antennas": {
        "requires": ["Ksp2Redux:Parts"],
        "conflicts": [],
        "provides": [],
        "recommended": true
    }
}
```

Each list is optional. To give the default feature more relations, use the mod id as the key:

```json
"features": {
    "BetterParts": { "requires": ["Ksp2Redux:Parts"] }
}
```

### From Lua

`PM:DefineFeature(id, options)` declares a feature of the mod that calls it. It operates only during patch registration. Thus, call it at the top level of a patch script. A Lua mod with a single file does not need a swinfo, because it declares its features with this function.

```lua
PM:DefineFeature("HardMode", {
    requires = { "Ksp2Redux:Parts" },
    conflicts = { "EasyMode" },
    recommended = false,
})
```

### From C#

A C# mod declares features through its Patch Manager scope, which is the scope that it uses for its patches. Call `DefineFeature` from `OnPreInitialized`, together with your patches:

```csharp
Patching.Mod.DefineFeature("HardMode", requires: new[] { "Ksp2Redux:Parts" });
```

### On the SDK Mod asset

The inspector of the Mod asset of a mod has a **Features** section:

* The default feature of the mod is always at the top, and shows `ModId:ModId`. Its **Requires**, **Conflicts** and **Provides** lists contain the relations of the default feature. These relations are in addition to the relations from the dependencies and conflicts of the mod.
* Under it, **+ Add Feature** adds a feature. Each feature has an id, a **Recommended** toggle, and the lists **Requires**, **Conflicts** and **Provides**. The fields in these lists autocomplete feature ids.

The SDK writes these features into the `features` section of the generated `swinfo.json`.

## Naming features for players

The menus show the name and the description of each feature. They read them from two localization keys:

```text
Feature/<ModId>/<FeatureId>/Name
Feature/<ModId>/<FeatureId>/Description
```

If a feature has no name key, the menus show its qualified id. The row of a mod shows the display name of the mod. The description of that row comes from `Feature/<ModId>/<ModId>/Description`. Put the keys in the localization CSV of your mod, the same as other keys:

```text
Key,Type,Desc,English
Feature/MyMod/HardMode/Name,Text,,Hard Mode
Feature/MyMod/HardMode/Description,Text,,"Halves engine thrust and doubles part costs"
```

## Which features a campaign has

### A new campaign

A new campaign starts with these features:

* each feature in the `Features` list of its [campaign pack](/doc/campaign-packs-5HBzAytKyU)
* the default features of Space Warp, Patch Manager and V-SwiFT
* each feature that the player selected under **Default Features** in the mods menu
* all the features that these features require

All other installed mods are **disabled** in the new campaign. Their default features are also disabled. A mod does not go into new campaigns only because it is installed. A mod goes into a campaign for one of these reasons:

* The pack lists it.
* The player selected it under **Default Features**.
* The player enabled it in the feature editor.

To make a mod enabled in its own campaigns, ship a pack that lists the features of the mod. The [2.5x Kerbolar System](/doc/worked-example-the-25x-kerbolar-system-Y2ansBiHVr) does this.

The Redux pack, `Default`, lists `Ksp2Redux:All`.

### Default Features in the mods menu

In the mods menu, the details of each mod have a **Default Features** foldout. The foldout contains:

* a toggle for each feature, with the default feature of the mod first
* an **Enable recommended** button, which selects the recommended features of the mod

All new campaigns that a player starts get the features that the player selects there. The foldout does not show the Redux features or the modding framework, because packs control the Redux content.

### The feature editor

On the new campaign menu, the **Edit Pack** button of the pack picker opens the **Feature Editor**. The editor shows the feature set of the selected pack. It lists all the features that can be enabled, grouped by mod, and has a search box:

* Each mod has a row for its default feature. The row expands to show the other features of the mod. Each mod row has its own **Enable recommended** button.
* When you enable a feature, the editor enables its requirements and locks them. The editor also disables and locks each feature that conflicts with it. When you disable a mod, the editor also disables its features.
* When you select a row, the editor shows the name, id and description of the feature, and the ids that it provides.
* The editor lists the problems with the feature set when they occur:
  * a requirement that no installed mod provides ("Missing provider for ...")
  * a requirement with more than one provider to select from ("Choose a provider for ...")
  * a real conflict, in red
* **Apply** keeps the changed feature set for the new campaign. **Cancel** discards it.

### Existing campaigns

On the campaign selection screen, **Edit Pack** opens the same editor with the feature set of the selected campaign. When you apply the changes, the editor writes the new feature set into the saves of that campaign. The game builds the new feature set the next time that the campaign loads.

### Loading a save with new mods

If a save loads with installed mods that the save never had, the game shows the differences. If the player continues to load the save, the feature editor opens as **New Mod Features**. The editor lists only the features of the new mods that the save can accept. At the start, the features that the player selected as **Default Features** are enabled. The save gets the features that the player enables there. A save never gets the features of a mod unless the player selects them.

### What a save stores

A save stores its requested features, its pack id and the versions of its mods. Each time the save loads, it uses exactly those features. If a mod is no longer installed, the save keeps its features as missing features. Thus, if the player installs the mod again, the save gets its features back.

The game migrates saves from before campaign features when they load. Such a save gets the `Features` of its pack and the default feature of each mod in its mod list. A save on the retired `Classic` pack migrates to `Default`. A sandbox save that selected a galaxy instead of a pack also migrates to `Default`.

### The main menu

The main menu uses `Ksp2Redux:All` and the default features of Space Warp, Patch Manager and V-SwiFT. Other mods are disabled in the main menu. Their assets that are not content still load, so a mod can show its UI and icons there.

The active feature set changes at three times:

* when the game boots into the main menu
* when a campaign loads
* when the player goes back to the main menu

## What a feature gates

| Content | What it needs |
|---------|---------------|
| Patch Manager patches | The mod's default feature, plus any `:Feature` / `:NoFeature` on the patch. |
| Created assets (`PM.JSON:New`, `NewResource`, `CreateGalaxy` and so on) | The same, on the created asset. |
| Prefab patches | The same, from `:Feature` / `:NoFeature` in code or the authoring metadata's feature lists. |
| A mod's Addressables content assets | The mod's default feature, plus every `feature:` label and no `nofeature:` label on the asset. |
| A mod's other Addressables assets, such as UI and icons | Every `feature:` label and no `nofeature:` label on the asset. Without labels, they load in all feature sets. |
| Patch run callbacks (`PM:OnBeginRun`, `PM:OnEndRun`) | The mod's default feature, plus any `:Feature` / `:NoFeature`. |
| Lua runtime hooks (`Game:Update`, `Game:OnFlight` and the rest) | The mod's default feature. |
| `Game:OnSetActivated`, `Game:OnCampaignLoaded`, `Game:OnCampaignUnloading` | The mod's default feature, plus any `:Feature` / `:NoFeature`. |
| C# `OnCampaignLoaded` / `OnCampaignUnloading` | The mod's default feature. |
| Config entries in the settings menu | The mod's default feature, plus any `:Feature` / `:NoFeature` on the entry. |
| Campaign settings (`CampaignConfig`) | The mod's default feature. |
| App bar buttons a mod registers | The mod's default feature. |

Campaign packs, game modes and difficulty settings are boot-time content. By design, they are the same in all feature sets and do not take feature gates. Patches and created assets under the `campaign_pack`, `game_mode`, `difficulty` and `difficulty_definition` labels always apply, in all feature sets. Thus, the game can show the pack of a mod before the mod is enabled.

### Disabled mods

A mod is disabled when its default feature is not in the active feature set. A disabled mod is still installed:

* Its assembly loads.
* The `OnPreInitialized`, `OnInitialized` and `OnPostInitialized` methods of its plugin run.
* Its Lua scripts run one time at boot to register.

Redux holds back all the items in the table above. Other actions that the mod does outside these hooks continue, for example Harmony patches or UI that the mod creates directly. These actions continue unless the mod checks `Enabled` (see [Runtime](#runtime) below).

## Redux features

Redux is the game itself, so it is enabled in all feature sets. Instead, Redux divides its content into features, so that a pack can exclude parts of that content:

| Feature | Requires | Covers |
|---------|----------|--------|
| `Ksp2Redux:Resources` |          | Resources and recipes. |
| `Ksp2Redux:Parts` | Resources | Parts and part audio presets. |
| `Ksp2Redux:Missions` | Parts    | Missions and mission granters. |
| `Ksp2Redux:Nodes` | Parts    | The tech tree: tech nodes and their icons. |
| `Ksp2Redux:Galaxy` | Resources | The galaxy and its bodies, science regions, discoverables, region maps, body resources, body cards, and atmosphere and cloud overrides. |
| `Ksp2Redux:Science` | Parts    | Science experiments. |
| `Ksp2Redux:Visuals` |          | Redux's visual changes: colored reentry gradients, cloud layers, drill dust colors, cryogenic frost flags, the Panther nozzle animation fix, Redux's engine plume and persistent smoke prefab patches, and Bop's scaled lighting. |
| `Ksp2Redux:Gameplay` |          | Redux's gameplay changes: the time warp altitude limits on airless bodies. |
| `Ksp2Redux:All` | every one above | Redux's whole game. |

The stock game content has no feature labels. If an untagged stock asset loads under one of these Addressables labels, it belongs to the feature next to the label:

| Label | Feature |
|-------|---------|
| `parts_data`, `part_audio_presets` | `Ksp2Redux:Parts` |
| `resources` | `Ksp2Redux:Resources` |
| `missions`, `missionGranters` | `Ksp2Redux:Missions` |
| `techNodeData`, `techNodeIcon` | `Ksp2Redux:Nodes` |
| `galaxy_definition`, `celestial_bodies`, `science_region`, `science_region_discoverables`, `science_region_map`, `cb_resources`, `cb_cards`, `atmosphere_overrides`, `volume_cloud_overrides` | `Ksp2Redux:Galaxy` |
| `scienceExperiment` | `Ksp2Redux:Science` |

Redux tags the content that it adds to the stock game with the same features. Redux gates its own patches on these features, and this includes its bug fixes. Thus, when a feature is disabled, the game removes the stock content and the Redux changes to it together.

Large mods can use this, and do not need to copy content:

* To replace the full galaxy, a mod ships a pack that lists the Redux features without `Ksp2Redux:Galaxy`, and the features of the mod. The pack names the galaxy of the mod as its `Galaxy`. The stock bodies do not load in that campaign.
* A part overhaul ships a pack that lists the Redux features without `Ksp2Redux:Parts`, `Ksp2Redux:Nodes` and the features that require them. The mod supplies its own parts and tech tree.
* If the content of a mod is applicable only with some Redux content, the default feature of the mod specifies this. For example, the default feature can require `Ksp2Redux:Parts`.

## Gating content on a feature

### Patches

Use `:Feature(...)` and `:NoFeature(...)` to gate Patch Manager patches, created assets, prefab patches and run callbacks. To gate a full block of registrations, use `PM:WithFeature`:

```lua
PM:WithFeature("HardMode", function()
    PM.Parts:Patch("HalveThrust")
        :Has("Module_Engine")
        :Do(function(part) ... end)
end)

PM.Parts:Patch("NormalThrustTweak"):NoFeature("HardMode"):Do(function(part) ... end)
```

Patches register one time, at boot. Each feature set gets its own patch run, with the patches that its features allow. Gate with these methods. Do not check `Features:Has` during registration. The [Features](/doc/features-geboWdIiDN) page under Core Concepts gives all the details:

* the AND and OR rules
* scopes
* created assets
* prefab patches
* run callbacks
* why checks during registration give incorrect results

### Assets built with the SDK

The content assets of a mod load only when the default feature of the mod is enabled. These assets are content:

* assets under one of the labels in the table above (`parts_data`, `missions`, `celestial_bodies` and so on)
* assets under a label that a mod loads with `Loading.AddSetLoadingAction`
* the input of a Patch Manager run, and prefab patch manifests

Other assets of a mod, such as its UI and icons, load in all feature sets, also in the main menu.

To make an asset also need a different feature of the mod, or a feature of a different mod, give the asset Addressables labels:

* `feature:<qualified id>`, such as `feature:MyMod:HardMode`: the asset needs that feature. If an asset has more than one `feature:` label, it needs **all** of these features.
* `nofeature:<qualified id>`: the asset does not load when that feature is enabled.

Only content assets need feature labels. Content assets are the assets that load under one of the labels in the table above (`parts_data`, `missions`, `celestial_bodies` and so on). The prefab, icon and textures of a part load because the JSON of the part names them. Thus, they follow the JSON and do not need their own labels.

You do not need to type these labels manually. The SDK has editors for them:

* **Modding > Asset Features...**, or **Open Asset Features** on the Mod asset, opens the **Asset Features** window. The window lists the content assets of a mod, with the features that each asset requires and excludes. To change the features of assets:

  
  1. Filter by kind, or search.
  2. Select the assets.
  3. Type a feature id.
  4. Click **Require**, **Exclude** or **Remove**.
* The **Core Part Data** inspector, the mission inspector and the **General** tab of the planet inspector each have a **Features** section. This section has a **Features** list and an **Excluded Features** list for that asset.
* The `PrefabPatchAuthoringMetadata` of a visual prefab patch has a **Required Features** list and an **Excluded Features** list.

Two enabled assets must never define the same item. If two loaded parts have the same `partName`, or two bodies have the same `bodyName`, the game keeps the first. It also logs an error that names the duplicate. Put alternative versions of an asset in features that conflict, or keep them apart with `nofeature:`. To change the stock version of an asset, patch it in place. Do not ship a second copy.

## Runtime

### Lua

Each mod script environment has these globals:

* `Features:Has(id)` returns whether a feature is enabled in the active feature set. A bare id refers to a feature of the calling mod.
* `Features:All()` returns the active feature set as a table that maps each id to `true`. Before the first feature set activates, it returns `nil`.
* `Features:OnChanged(fn)` calls `fn` with the new feature set after each feature set activates. It calls `fn` only when the calling mod is enabled in that feature set.
* `Enabled` is `true` when the default feature of the mod is enabled. Its value changes when the feature set changes.
* `CampaignConfig` operates like `Config`, but each campaign keeps its own values. See [Config integration](/doc/config-integration-aTM54Tfqkz).

The campaign hooks of the `Game` global return a gated action. Thus, you can chain `:Feature(id)` and `:NoFeature(id)` on them, with one id in each call:

* `Game:OnSetActivated(fn)` runs each time a feature set becomes active. Use it for data that a mod keeps for each feature set. The hook gives the feature set to `fn`.
* `Game:OnCampaignLoaded(fn)` runs each time a campaign finishes loading, if the mod is enabled in that campaign. This includes quickloads.
* `Game:OnCampaignUnloading(fn)` runs before that campaign unloads.

```lua
Game:OnCampaignLoaded(function()
    Log:Info("hard mode campaign loaded")
end):Feature("HardMode")
```

A config entry shows in the settings menu only when its features allow it. To set these features, use `:Feature(id)` and `:NoFeature(id)` on the builder. Entries that you define in `PM:WithFeature` automatically get the feature of that scope.

### C#

* `Enabled` on `GeneralMod`, `MonoBehaviourMod` and `KerbalMod` is `true` when the default feature of the mod is enabled in the active feature set. Check it before each action that the mod must do only when it is enabled.
* Put `[Feature("Id")]` on a `bool` field of your plugin class. The field then always shows whether the feature is enabled. A bare id refers to a feature of your mod. The game writes the field each time a feature set activates.
* `OnCampaignLoaded()` and `OnCampaignUnloading()` are virtual methods on the same base classes. They run only for campaigns in which the mod is enabled. In `OnCampaignUnloading`, undo all the changes that `OnCampaignLoaded` made.
* `SpaceWarp2.API.Features.ActiveFeatures` reads the active feature set. It has these members:
  * `Enabled`: the enabled ids, or `null` before a feature set activates
  * `IsEnabled(id)`
  * `IsModEnabled(modId)`
  * `Changed`, an event of type `Action<IReadOnlyCollection<string>?>`. It occurs after each feature set activates, and gives the enabled ids.
* `[ConfigFeature("Id")]` and `[ConfigNoFeature("Id")]` on a `[ConfigValue]` show that value in the settings menu only when the feature is enabled or disabled.
* `SWMetadata.CampaignConfigFile` is the config file of the mod for each campaign.

```csharp
using SpaceWarp2.API.Features;
using SpaceWarp2.API.Mods;

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

    public override void OnInitialized()
    {
        ActiveFeatures.Changed += _ => SWLogger.LogInfo($"Enabled: {Enabled}, hard mode: {_hardMode}");
    }

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

Gate C# patches the same as Lua patches. Use `.Feature(...)` and `.NoFeature(...)` on the fluent builder. On a patch of the attribute type, use `[NeedsFeature(...)]` and `[NoFeature(...)]`. See [Patching from C#](/doc/patching-from-c-729VnFfTeV).

## The patch cache

Patch Manager keeps a separate cache for each feature set that it patched. The caches share identical archives. When you change to a campaign with a feature set that Patch Manager built before, the content loads directly from the cache. See [Caching and invalidation](/doc/caching-and-invalidation-QifFi4oev9).

## Updating an existing mod

These changes apply to mods made before campaign features:

* `**PM.InvalidatesOnChange**` **is removed.** Config values never cause a patch rebuild. If a player enables or disables content for each campaign, that content must be a feature.
* **The** `**Classic**` **pack is removed.** Saves on it migrate to `Default`.
* **New campaigns do not automatically include installed mods.** See [A new campaign](#a-new-campaign).

To update a mod:


1. **Declare your features.** Make one feature for each part of the content that a player can want without the rest. Give the features names with localization keys.
2. **Replace patches that use config values.** If a patch reads a config value to decide whether it runs, change it into a patch gated on a feature. Continue to use config for runtime behavior.
3. **Gate your C# on** `**Enabled**`**.** Your plugin must check `Enabled` before each action outside the hooks that Redux gates.
4. **Tag optional assets.** If SDK content belongs to one of your features, give it `feature:` labels in the Asset Features window.

## Worked example

The [2.5x Kerbolar System](/doc/worked-example-the-25x-kerbolar-system-Y2ansBiHVr) is a full small mod that uses features. It has:

* two features, declared in `swinfo.json`
* rescale patches that all apply in place under one of these features
* a campaign pack that enables these features together with `Ksp2Redux:All`

Campaigns without its features keep the Redux galaxy with no changes.