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 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
dependencieslist of theswinfo.jsonof the mod.It conflicts with the default feature of each mod in the
conflictslist.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 |
|---|---|
| When this feature is enabled, these features are also enabled. Their own requirements are also enabled, and so on. |
| These features cannot be enabled in the same campaign as this feature. |
| This feature replaces these ids when the game resolves requirements. |
| The mod recommends this feature. The Enable recommended buttons enable it. |
The game resolves the relations with these rules:
Requirements are transitive. When
KerbolarUpscale:Rescaleis enabled,KerbolarUpscale:KerbolarUpscale(the default feature of the mod) andKsp2Redux:Galaxyare also enabled.Ksp2Redux:GalaxyrequiresKsp2Redux:Resources, soKsp2Redux:Resourcesis 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:LifeSupportcannot 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,conflictsandprovideslists 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.
"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:
"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.
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:
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:
Feature/<ModId>/<FeatureId>/Name
Feature/<ModId>/<FeatureId>/DescriptionIf 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:
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
Featureslist of its campaign packthe 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 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 |
Created assets ( | The same, on the created asset. |
Prefab patches | The same, from |
A mod's Addressables content assets | The mod's default feature, plus every |
A mod's other Addressables assets, such as UI and icons | Every |
Patch run callbacks ( | The mod's default feature, plus any |
Lua runtime hooks ( | The mod's default feature. |
| The mod's default feature, plus any |
C# | The mod's default feature. |
Config entries in the settings menu | The mod's default feature, plus any |
Campaign settings ( | 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,OnInitializedandOnPostInitializedmethods 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 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 |
|---|---|---|
| Resources and recipes. | |
| Resources | Parts and part audio presets. |
| Parts | Missions and mission granters. |
| Parts | The tech tree: tech nodes and their icons. |
| Resources | The galaxy and its bodies, science regions, discoverables, region maps, body resources, body cards, and atmosphere and cloud overrides. |
| Parts | Science experiments. |
| 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. | |
| Redux's gameplay changes: the time warp altitude limits on airless bodies. | |
| 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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
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 itsGalaxy. The stock bodies do not load in that campaign.A part overhaul ships a pack that lists the Redux features without
Ksp2Redux:Parts,Ksp2Redux:Nodesand 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:
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 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_bodiesand so on)assets under a label that a mod loads with
Loading.AddSetLoadingActionthe 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 asfeature:MyMod:HardMode: the asset needs that feature. If an asset has more than onefeature: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:
Filter by kind, or search.
Select the assets.
Type a feature id.
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
PrefabPatchAuthoringMetadataof 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 totrue. Before the first feature set activates, it returnsnil.Features:OnChanged(fn)callsfnwith the new feature set after each feature set activates. It callsfnonly when the calling mod is enabled in that feature set.Enabledistruewhen the default feature of the mod is enabled. Its value changes when the feature set changes.CampaignConfigoperates likeConfig, but each campaign keeps its own values. See Config integration.
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 tofn.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.
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#
EnabledonGeneralMod,MonoBehaviourModandKerbalModistruewhen 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 aboolfield 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()andOnCampaignUnloading()are virtual methods on the same base classes. They run only for campaigns in which the mod is enabled. InOnCampaignUnloading, undo all the changes thatOnCampaignLoadedmade.SpaceWarp2.API.Features.ActiveFeaturesreads the active feature set. It has these members:Enabled: the enabled ids, ornullbefore a feature set activatesIsEnabled(id)IsModEnabled(modId)Changed, an event of typeAction<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.CampaignConfigFileis the config file of the mod for each campaign.
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#.
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.
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 toDefault.New campaigns do not automatically include installed mods. See A new campaign.
To update a mod:
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.
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.
Gate your C# on
**Enabled**. Your plugin must checkEnabledbefore each action outside the hooks that Redux gates.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 is a full small mod that uses features. It has:
two features, declared in
swinfo.jsonrescale 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.