# Migrating from stock KSP2

There have been many significant changes to SpaceWarp and the general modding architecture from stock KSP2 to KSP2 Redux, and old code mods will *not* work without some modifications. This page serves as a general overview of the changes and how to upgrade your mods.

# Overview

* **BepInEx removal**: BepInEx has been completely removed. Equivalents for most of its functionality are now located in the `ReduxLib` or `SpaceWarp2` namespaces.
* **Harmony version**: HarmonyX has been replaced with Harmony2. A `CreateAndPatchAll` utility method is now provided within the mod classes.
* **Part modules**: Classes extending `PartComponentModule` now need to explicitly subscribe to updates if they implement the `OnUpdate` method.
* **API cleanup**: Deprecated APIs from SpaceWarp 1 have been removed. A legacy package for porting old mods is available at [https://github.com/KSP2Community/SpaceWarpLegacy,](https://github.com/KSP2Community/SpaceWarpLegacy) though it is not recommended for new projects.
* **Namespace changes**: The namespace `SpaceWarp` has been replaced with `SpaceWarp2`. Additionally, namespaces previously named as `SpaceWarp.API.<Module>` are now named `SpaceWarp2.<Module>.API`.
* **Campaign features**: An installed mod is not on in all campaigns. Each campaign has its own feature set of [features](/doc/campaign-features-9YIewcIArR). If the default feature of a mod is off in a campaign, the mod must do nothing in that campaign. See [Mods that are off in a campaign](#mods-that-are-off-in-a-campaign).

# BepInEx API

## BaseUnityPlugin and BaseSpaceWarpPlugin

The base mod classes `BaseUnityPlugin` and `BaseSpaceWarpPlugin` have been replaced with 3 classes, along with an interface that can be used to define more.

For all of these types, the mod lifecycle virtual methods from SpaceWarp 1 are the same, so `OnPreInitialized`, `OnInitialized`, and `OnPostInitialized` remain the same with the same semantics.

### `SpaceWarp2.API.Mods.ISpaceWarpMod`

This is the base mod interface that can be used to define more mod types if desired. It provides three common properties:

* `**SWLogger**` - logging interface for your mod,
* `**SWConfiguration**` - adding and reading mod configuration values,
* `**SWMetadata**` - provides metadata about your mod.

### `SpaceWarp2.API.Mods.GeneralMod`

This is the recommended mod class for mods that don’t need the Unity lifecycle (e.g. for mods that only run some initialization code or provide part component classes, and don’t need Update or FixedUpdate).

### `SpaceWarp2.API.Mods.MonoBehaviourMod`

This is the closest equivalent to `BaseUnityPlugin`. It is an `ISpaceWarpMod` that is also a MonoBehaviour, and as such, has the entire Unity component lifecycle. It gets instantiated as soon as possible upon loading the game.

### `Redux.ExtraModTypes.KerbalMod`

This is the closest equivalent to `BaseSpaceWarpPlugin`. It includes `Game`, `Assets`, `Messages` and `CFXSystem` accessors in addition to everything provided by `MonoBehaviourMod`.

## Mods that are off in a campaign

Each Redux campaign uses a feature set of [campaign features](/doc/campaign-features-9YIewcIArR). Each mod has a default feature. This feature is on when the mod is on.

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

* Its assembly loads.
* `OnPreInitialized`, `OnInitialized` and `OnPostInitialized` run as before.

For a mod that is off, Redux disables its patches, assets, campaign hooks and app bar buttons. Code that your mod runs directly continues to run, for example Harmony patches.

All three base classes supply these members:

* `Enabled` is `true` while the default feature of your mod is on in the active feature set. Check it before code that must run only while your mod is on.
* `OnCampaignLoaded()` and `OnCampaignUnloading()` run only for campaigns that have your mod on. Set up per-campaign behaviour in `OnCampaignLoaded()`. Remove it in `OnCampaignUnloading()`.
* `[Feature("Id")]` on a `bool` field makes the field show if one of the features of your mod is on.

For all of the members, see [Code modding guides](/doc/code-modding-guides-SSQ735ZM52).

## Logging

The BepInEx logging system has been replaced with the `ReduxLib.Logging` namespace. To access your mod’s provided logger, use the `SWLogger` property in your mod class. To create a new logger, you can call `ReduxLib.ReduxLib.GetLogger(name)`.

Log output from here goes to `redux.log` in the base folder of the install. There is an option in the game’s Settings → Mods → Redux to also mirror the output into Unity’s logs (and console).

## Configuration

The BepInEx configuration system has been replaced with the `ReduxLib.Configuration` namespace. It functions slightly differently than BepInEx’s, the following snippet illustrates a general use case:

```csharp
/* This is a set of code snippets for defining a config value */

// Define a config section and value in your mod class.
// The "string" type can be replaced by any type that can be serialized to a JSON
[ConfigSection("My Section", loc: "MyMod/Settings/MySection")]
[ConfigValue(
    "Some Config Name",
    "Some Config Description",
    nameLoc: "MyMod/Settings/Name/SomeConfigValue",
    descLoc: "MyMod/Settings/Desc/SomeConfigValue"
)]
public ConfigValue<string> SomeConfigValue = new ConfigDescription<string>("DEFAULT VALUE");

// If you only need a one-way binding (no callback on value change, etc), you can
// directly use the raw type for the config value object
[ConfigValue(
    "Some Other Config Name",
    "Some Other Config Description",
    nameLoc: "MyMod/Settings/Name/SomeOtherConfigValue",
    descLoc: "MyMod/Settings/Desc/SomeOtherConfigValue"
)]
public bool SomeOtherConfigValue = true;

// ...

public override void OnPreInitialized()
{
    // We bind the configs defined in the class to the SWConfiguration object
    // (inherited from the Mod base class)
    SWConfiguration.Bind(this);

    // Register an action to call when the value changes
    SomeConfigValue.RegisterCallback(SomeConfigCallback);
}

private static void SomeConfigCallback(string oldValue, string newValue)
{
    SWLogger.LogInfo($"I changed from {oldValue} to {newValue}");
}
```

## Keybinds

New in KSP2 Redux, ReduxLib provides classes for binding KeyboardShortcuts and Keybinds. When you create config values with these types, they will be automatically added into the Settings menu.

### `ReduxLib.Input.Keybind`

Represents a single key action

### `ReduxLib.Input.KeyboardShortcut`

Represents a keyboard shortcut consisting of modifier(s) and a single key

# HarmonyX

HarmonyX has been replaced with a modified version of Harmony2 in order to allow it to run in the Unity Editor. Unfortunately, this means that some features were removed.

`CreateHarmonyAndPatchAll` is a helper method provided in most mod classes, which replaces `Harmony.CreateAndPatchAll`. It will create a harmony instance and apply all patches from your mod’s assembly.

# Part Modules

The `OnUpdate` method in classes extending `KSP.Sim.impl.PartComponentModule` no longer runs automatically for performance reasons. If you need it to run, you will now need to explicitly register and unregister for updates using `KSP.Sim.impl.PartComponent`’s `RegisterModuleForUpdates` and `UnregisterModuleForUpdates` methods. Following is an example:

```csharp
public class PartComponentModule_MyModule : PartComponentModule
{
    public override void OnStart(double universalTime)
    {
        Part.RegisterModuleForUpdates(this);
    }
    
    public override void OnUpdate(double universalTime, double deltaUniversalTime)
    {
        // My update code will run with this setup
    }
    
    public override void OnShutdown()
    {
        Part.UnregisterModuleForUpdates(this);
    }
}
```

# Deprecations

The following APIs have been deprecated and removed in Redux:

* The `AssetManager` API from SpaceWarp 1 has been deprecated and removed in favor of Unity’s [Addressables](https://docs.unity3d.com/Packages/com.unity.addressables@1.28/manual/index.html), which the Redux SDK can automatically build.
* The IMGUI API and skin from SpaceWarp 1 has been deprecated and removed in favor of [UI Toolkit](https://docs.unity3d.com/2022.3/Documentation/Manual/UIElements.html), which the Unity Editor has a convenient editor for. The [UitkForKsp2.Unity](https://github.com/UitkForKsp2/UitkForKsp2.Unity) Unity package provides a KSP2 theme, fonts and other assets.


:::info
If you need either of the two above deprecated APIs, a legacy port is available in the [SpaceWarpLegacy Unity package](https://github.com/KSP2Community/SpaceWarpLegacy).

:::

* The Lua modding support from SpaceWarp 1 was completely removed without any replacements, as it never got used by any mods.

# Namespace Changes

The following namespaces have been renamed or moved:

* `SpaceWarp` → `SpaceWarp2`
* `SpaceWarp.API.Configuration` → `ReduxLib.Configuration`
* `SpaceWarp.API.Loading` → `SpaceWarp2.API.Loading`
* `SpaceWarp.API.Logging` → `ReduxLib.Logging`
* `SpaceWarp.API.Mods` → `SpaceWarp2.API.Mods`
* `SpaceWarp.API.Parts` → `SpaceWarp2.API.Parts`
* `SpaceWarp.API.SaveGameManager` → `SpaceWarp2.API.SaveGameManager`
* `SpaceWarp.API.Versions` → `SpaceWarp2.API.Versions`
* `SpaceWarp.Patching.LoadingActions` → `SpaceWarp2.API.Loading.LoadingActions`
* `SpaceWarp.API.Game.Messages` → `Redux.Messages`
* `SpaceWarp.API.Game.Extensions` → integrated into the target classes
* `SpaceWarp.API.Game.Waypoints` → `SpaceWarp2.Game.API.Waypoints`
* `SpaceWarp.API.Messaging` → `ReduxLib.Messaging`
* `SpaceWarp.API.Sound` → `SpaceWarp2.Sound.API`
* `SpaceWarp.API.UI` → `SpaceWarp2.UI.API`

# UI Toolkit

From Unity 6.6 on, creating custom controls using the `UxmlFactory` and `UxmlTraits` pattern is no longer possible, so you will need to migrate to the newer way of using `[UxmlElement]` and `[UxmlAttribute]` attributes. For more info, see [Unity’s documentation](https://docs.unity3d.com/6000.6/Documentation/Manual/ui-systems/migrate-custom-control.html).

There have also been many changes made to the KerbalUI theme, so make sure your UI still looks good with these new styles.

# Example before & after

Before:

```csharp
using System.Reflection;
using BepInEx;
using JetBrains.Annotations;
using SpaceWarp;
using SpaceWarp.API.Assets;
using SpaceWarp.API.Mods;
using SpaceWarp.API.UI.Appbar;
using SpaceWarpModUI.UI;
using UitkForKsp2.API;
using UnityEngine;
using UnityEngine.UIElements;

namespace SpaceWarpModUI;

[BepInPlugin(MyPluginInfo.PLUGIN_GUID, MyPluginInfo.PLUGIN_NAME, MyPluginInfo.PLUGIN_VERSION)]
[BepInDependency(SpaceWarpPlugin.ModGuid, SpaceWarpPlugin.ModVer)]
public class SpaceWarpModUIPlugin : BaseSpaceWarpPlugin
{
    // Useful in case some other mod wants to use this mod a dependency
    [PublicAPI] public const string ModGuid = MyPluginInfo.PLUGIN_GUID;
    [PublicAPI] public const string ModName = MyPluginInfo.PLUGIN_NAME;
    [PublicAPI] public const string ModVer = MyPluginInfo.PLUGIN_VERSION;

    /// Singleton instance of the plugin class
    [PublicAPI] public static SpaceWarpModUIPlugin Instance { get; set; }

    // AppBar button IDs
    internal const string ToolbarFlightButtonID = "BTN-SpaceWarpModUIFlight";
    internal const string ToolbarOabButtonID = "BTN-SpaceWarpModUIOAB";
    internal const string ToolbarKscButtonID = "BTN-SpaceWarpModUIKSC";

    /// <summary>
    /// Runs when the mod is first initialized.
    /// </summary>
    public override void OnInitialized()
    {
        base.OnInitialized();

        Instance = this;

        // Load all the other assemblies used by this mod
        LoadAssemblies();

        // Load the UI from the asset bundle
        var myFirstWindowUxml = AssetManager.GetAsset<VisualTreeAsset>(
            // The case-insensitive path to the asset in the bundle is composed of:
            // - The mod GUID:
            $"{ModGuid}/" +
            // - The name of the asset bundle:
            "SpaceWarpModUI_ui/" +
            // - The path to the asset in your Unity project (without the "Assets/" part)
            "ui/myfirstwindow/myfirstwindow.uxml"
        );

        // Create the window options object
        var windowOptions = new WindowOptions
        {
            // The ID of the window. It should be unique to your mod.
            WindowId = "SpaceWarpModUI_MyFirstWindow",
            // The transform of parent game object of the window.
            // If null, it will be created under the main canvas.
            Parent = null,
            // Whether or not the window can be hidden with F2.
            IsHidingEnabled = true,
            // Whether to disable game input when typing into text fields.
            DisableGameInputForTextFields = true,
            MoveOptions = new MoveOptions
            {
                // Whether or not the window can be moved by dragging.
                IsMovingEnabled = true,
                // Whether or not the window can only be moved within the screen bounds.
                CheckScreenBounds = true
            }
        };

        // Create the window
        var myFirstWindow = Window.Create(windowOptions, myFirstWindowUxml);
        // Add a controller for the UI to the window's game object
        var myFirstWindowController = myFirstWindow.gameObject.AddComponent<MyFirstWindowController>();

        // Register Flight AppBar button
        Appbar.RegisterAppButton(
            ModName,
            ToolbarFlightButtonID,
            AssetManager.GetAsset<Texture2D>($"{ModGuid}/images/icon.png"),
            isOpen => myFirstWindowController.IsWindowOpen = isOpen
        );

        // Register OAB AppBar Button
        Appbar.RegisterOABAppButton(
            ModName,
            ToolbarOabButtonID,
            AssetManager.GetAsset<Texture2D>($"{ModGuid}/images/icon.png"),
            isOpen => myFirstWindowController.IsWindowOpen = isOpen
        );

        // Register KSC AppBar Button
        Appbar.RegisterKSCAppButton(
            ModName,
            ToolbarKscButtonID,
            AssetManager.GetAsset<Texture2D>($"{ModGuid}/images/icon.png"),
            () => myFirstWindowController.IsWindowOpen = !myFirstWindowController.IsWindowOpen
        );
    }

    /// <summary>
    /// Loads all the assemblies for the mod.
    /// </summary>
    private static void LoadAssemblies()
    {
        // Load the Unity project assembly
        var currentFolder = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory!.FullName;
        var unityAssembly = Assembly.LoadFrom(Path.Combine(currentFolder, "SpaceWarpModUI.Unity.dll"));
        // Register any custom UI controls from the loaded assembly
        CustomControls.RegisterFromAssembly(unityAssembly);
    }
}
```

After:

```csharp
using JetBrains.Annotations;
using Redux.ExtraModTypes;
using SpaceWarp2.UI.API.Appbar;
using SpaceWarpModUI.UI;
using UitkForKsp2.API;
using UnityEngine;
using UnityEngine.UIElements;

namespace SpaceWarpModUI;

public abstract class SpaceWarpModUIPlugin : KerbalMod
{
    [PublicAPI] public const string ModGuid = "SpaceWarpModUI"; // ModGuid here
    [PublicAPI] public const string ModName = "SpaceWarp UI Mod"; 
    //[PublicAPI] public const string ModVer = MyPluginInfo.PLUGIN_VERSION;

    /// Singleton instance of the plugin class
    [PublicAPI] public static SpaceWarpModUIPlugin Instance { get; set; }

    // AppBar button IDs
    internal const string ToolbarFlightButtonID = "BTN-SpaceWarpModUIFlight";
    internal const string ToolbarOabButtonID = "BTN-SpaceWarpModUIOAB";
    internal const string ToolbarKscButtonID = "BTN-SpaceWarpModUIKSC";

    /// <summary>
    /// Runs before the game is loaded, as early as possible. 
    /// You would want to use this to register loading actions (like loading JSONs from addressables).
    /// </summary>

    public override void OnPreInitialized()
    {
        Debug.Log("Pre Initialized");
    }
    
    /// <summary>
    /// Runs after the game is loaded and after your mod assets are loaded.
    /// </summary>

    public override async void OnInitialized()
    {
        Debug.Log("Is Initializing"); // You can use Debug.Log to log things.
        Instance = this;

        // Start both asset operations before awaiting either one so that they
        // can load concurrently.
        var myFirstWindowUxmlHandle =
            Assets.LoadAssetAsync<VisualTreeAsset>(
                // The address given to the addressable asset:
                "/myfirstwindow/myfirstwindow.uxml"
            );

        var appbarIconHandle =
            Assets.LoadAssetAsync<Texture2D>(
                $"{ModGuid}/images/icon.png"
            );

        try
        {
            // Await the Tasks exposed by the Unity operation handles.
            // This does not block the Unity main thread.
            await Task.WhenAll(
                myFirstWindowUxmlHandle.Task,
                appbarIconHandle.Task
            );
        }
        catch (Exception exception)
        {
            Debug.LogError(
                $"{ModName}: An exception occurred while loading UI assets."
            );

            Debug.LogException(exception);
            return;
        }

        if (myFirstWindowUxmlHandle.Status !=
            AsyncOperationStatus.Succeeded)
        {
            Debug.LogError(
                $"{ModName}: Failed to load the window UXML asset. " +
                $"Status: {myFirstWindowUxmlHandle.Status}. " +
                $"Exception: {myFirstWindowUxmlHandle.OperationException}"
            );

            return;
        }

        if (appbarIconHandle.Status != AsyncOperationStatus.Succeeded)
        {
            Debug.LogError(
                $"{ModName}: Failed to load the AppBar icon. " +
                $"Status: {appbarIconHandle.Status}. " +
                $"Exception: {appbarIconHandle.OperationException}"
            );

            return;
        }

        // Both operations have completed successfully, so we can access Result now.
        var myFirstWindowUxml = myFirstWindowUxmlHandle.Result;
        var appbarIcon = appbarIconHandle.Result;

        // Create the window options object
        var windowOptions = new WindowOptions
        {
            // The ID of the window. It should be unique to your mod.
            WindowId = "SpaceWarpModUI_MyFirstWindow",
            // The transform of parent game object of the window.
            // If null, it will be created under the main canvas.
            Parent = null,
            // Whether the window can be hidden with F2.
            IsHidingEnabled = true,
            // Whether the UI will use the "Scale" option in the settings.
            UseStockScale = true,
            // Whether to disable game input when typing into text fields.
            DisableGameInputForTextFields = true,
            MoveOptions = new MoveOptions
            {
                // Whether the window can be moved by dragging.
                IsMovingEnabled = true,
                // Whether the window can only be moved within the screen bounds.
                CheckScreenBounds = true
            }
        };

        // Create the window
        var myFirstWindow = Window.Create(windowOptions, myFirstWindowUxml);
        // Add a controller for the UI to the window's game object
        var myFirstWindowController = myFirstWindow.gameObject.AddComponent<MyFirstWindowController>();

        // Register Flight AppBar button
        Appbar.RegisterAppButton(
            ModName,
            ToolbarFlightButtonID,
            appbarIcon,
            isOpen => myFirstWindowController.IsWindowOpen = isOpen
        );

        // Register OAB AppBar Button
        Appbar.RegisterOABAppButton(
            ModName,
            ToolbarOabButtonID,
            appbarIcon,
            isOpen => myFirstWindowController.IsWindowOpen = isOpen
        );

        // Register KSC AppBar Button
        Appbar.RegisterKSCAppButton(
            ModName,
            ToolbarKscButtonID,
            appbarIcon,
            () => myFirstWindowController.IsWindowOpen = !myFirstWindowController.IsWindowOpen
        );
    }

    /// <summary>
    /// Runs after all mods have done their 2nd stage initialization.
    /// You would want to use this to interact with game systems, since they're guaranteed to be loaded on this stage.
    /// </summary>

    public override void OnPostInitialized()
    {
        Debug.Log("Post Initialized");
    }


    // You can create controls directly in Unity. No need for this method anymore.
    //private static void LoadAssemblies()
    //{
    //    // Load the Unity project assembly
    //    var currentFolder = new FileInfo(Assembly.GetExecutingAssembly().Location).Directory!.FullName;
    //    var unityAssembly = Assembly.LoadFrom(Path.Combine(currentFolder, "SpaceWarpModUI.Unity.dll"));
    //    // Register any custom UI controls from the loaded assembly
    //    CustomControls.RegisterFromAssembly(unityAssembly);
    //}
}
```

For more examples you can look at some of the "Reduxed" mods we've made for Redux here: <https://github.com/orgs/KSP2Community/repositories?type=all>