Diazo

Getting started, the basics of writing a plug-in.

Recommended Posts

The basics, getting started with writing plug-ins.

 

Welcome to a quickstart guide on how to start writing a code plug-in for Kerbal Space Program.

 

This is a quick guide intended as an overview on getting started and what hooks are available in KSP where you can attach your code. It is not intended as a step-by-step tutorial, rather it is intended to make you aware of your options so you can then investigate in more detail which options you wish to use.

 

  1. IDE Setup
  2. The hooks.
    1. KSPAddon
    2. ScenarioModule
    3. VesselModule
    4. PartModule
  3. Common Methods
  4. Common References
  5. On-Screen GUI
  6. Toolbar button
  7. GameEvents System
  8. ConfigNode overview
  9. Save/Load Data

 

1) IDE Setup

Writing a code plug-in for KSP is done in C# so we need a program (IDE) to write code in that can produce the .dll file from that code that KSP will recognize.

Any program that can make .dll files will work, this document uses Microsoft Visual Studio for its examples.

Once the IDE program is installed, start a new project and make sure you set the “.Net Version 3.5”. If you do not the project will compile for the most recent version of .Net which is 4.5 and KSP will not handle it correctly and throw errors when it tries to load.

We then need to tell the IDE where to find the References to the KSP files. In your project, add a reference to UnityEngine.dll, UnityEngine.UI.dll, Assembly-Csharp.dll, Assembly-Csharp-firstpass.dll. These files are found in the “KSP_Install\KSP_x64_Data\Managed”.

Then at the top, add a line below “using System;” that says “using UnityEngine”. Our blank project is now ready to go and we can start coding.

 

To test, copy-paste the following code into your program:

using System;
using UnityEngine;

namespace HelloWorld
{
    [KSPAddon(KSPAddon.Startup.MainMenu, false)]
    public class Hello : MonoBehaviour
    {
        public void Update()
        {
            Debug.Log("Hello world! " + Time.realtimeSinceStartup);
        }
    }
}

then compile your .dll file and place it in the KSP_Install\GameData folder. It can be in a sub-folder, typically you will make a sub-folder for your mod in order for the players to keep things straight.

 

When you start KSP and reach the main menu, if everything is setup correctly, the code above will spam "Hello world!" to the console which is toggled on with Alt-F12. It will also append the time since the game was started so you can see the lines of text change.

 

2) Basic hooks

In order to actually do anything, we need to hook into KSP so that our code will run. There are 4 ways to do this, which one you use will depend on what purpose your code has. Note that using multiple methods for different pieces of code is a valid approach.

 

KSPAddon

The KSPAddon hook is used when you want to run some code that is attached to a specific scene. Use this when you code runs and affect the Flight scene, the Tracking Station scene, the Editor scene, and so on. KSP will automatically create an instance of a class tagged this way at the start of the scene and destroy it at the end of the scene. Note: There is no integrated save/loading of data in the KSPAddon hook, you must do that manually.

 

Use cases: Permanent changes to how the scene works, either the game-space itself or the UI. As there is no save/loading of data, the changes need to be the same every time.

 

KSPAddon(Scene,Dont_Destroy)

Scene: The Scene enumerator, in which scene does an instance of this class get loaded. This takes values from the KSPAddon.Startup enum and multiple values to start in multiple scenes can be selected.

Dont_Destroy: Don’t destroy the class when the scene ends. Highly recommended to leave false until you understand how Unity creates/destorys objects in and across different scenes. Is a boolean value.

[KSPAddon(KSPAddon.Startup.Flight, false)]
public class MyKSPAddon : MonoBehavior
{
    //your code here.
}

This will tell KSP to spawn an instance of MyKSPAddon when the Flight scene starts and destroy it when the Flight scene ends. It is high recommended that the class inherit from MonoBehavior (or other class that inherits from MonoBehavior) so that the Common Methods are available to you.

 

ScenarioModule

The Scenario Module is used for when a mod needs to affect a large part of the game and provides the ability to Save/Load data. One caveat is that different scenes have different objects so make sure to check what scene you are in before referencing objects that exist in multiple scenes.

 

ScenarioModule(ScenarioCreatingOptions, Gamescene)

ScenarioCreationOptions: What type of games do we add this to? Take from the ScenarioCreateOptions enum.

GameScene: The Scene enumerator, in which scene does an instance of this class get loaded? Takes from the GameScenes enum.

[KSPScenario(ScenarioCreationOptions.AddToNewCareerGames | ScenarioCreationOptions.AddToExistingCareerGames, GameScenes.SPACECENTER)]
class MyScenario : ScenarioModule
{
	//your code here
}

 

This will add you ScenarioModule to all games in career mode and create an instance of this class in the Space Center scene. Note that it must inherit ScenarioModule.

 

 

VesselModule

An single instance of a VesselModule is automatically added to every vessel that exists (can be switched to in the tracking station). Note that VesselModule's will still execute for unloaded vessels which PartModules will not.

public class MyVesselModule : VesselModule
{
    //your code here.
}

A VesselModule must inherit from VesselModule and no Attribute is required on the preceding line. Note that while a VesselModule does save/load data it does not have any provision for transfer of data between two VesselModules, such as when two vessels dock and combine, or an undock happens and a single vessel becomes two.

 

 

PartModule

A PartModule is a class that gets attached to a part. This is done through the use of ModuleManager (another mod) and scripts that control which parts your PartModule gets attached to. These scripts are very flexible and you can attach only to your desired parts and attach multiple instance of your PartModule if you desire. Module Manager download, and instructions on how to use, can be found here.

public class MyPartModule : PartModule
{
    //your code here.
}

A PartModule must inherit from PartModule.

 

3) Common Methods

There are several common methods inside these 4 hooks that KSP will automatically execute for you. (This section assumes KSPAddon inherits from MonoBehavior.)
 

public void Awake() 
{
}

KSPAddon, ScenarioModule, VesselModule, PartModule

Called once and runs when an instance of the class is first created. Not recommended in most cases as it may not run when you expect as this is not tied to a scene. Not recommended at all in PartModule as KSP uses Awake() in a PartModule for its own purposes.

 

public void Start()
{
}

KSPAddon, ScenarioModule, VesselModule, PartModule

Called once and is where any setup or “on-spawn” code should go. This method runs when your class spawns into the correct scene and runs before all other methods (except Awake()). Note that all Start() methods that need to run in a scene run sequentially, if you are trying to reference another object it may not exist yet and could require using a Coroutine to delay running some of your code.

 

public void Update()
{
}

KSPAddon, ScenarioModule, VesselModule, PartModule

Called every update frame and is in sync with the actual frame that gets drawn on the monitor. If KSP is running at 60fps, this method will get called 60 times a second, once for each frame on the monitor. Anything code relating to the UI or the player interacts with should go here.

 

public void FixedUpdate()
{
}

KSPAddon, ScenarioModule, VesselModule, PartModule

Called every physics frame and anything physics or game related should happen here.

 

Caution: Update and FixedUpdate are NOT synchronized. Depending on current frame rates and demand on the CPU, multiple (or no) Update frames could happen between two FixedUpdate frames, or multiple (or no) FixedUpdate frames could happen between two Update frames.

 

public void OnDisable()
{
}

KSPAddon, ScenarioModule, VesselModule, PartModule

Runs when the class is destroyed. Important for unsubscribing from GameEvents and destroying Toolbar buttons.

 

 

Public void OnSave(ConfigNode node)
{
}

ScenarioModule, VesselModule, PartModule

Run when a game save happens. You can add your data to the node object in order to load it later. Note that any KSPFields are automatically save/loaded and do not require using this method.

 

public void OnLoad(ConfigNode node)
{
}

ScenarioModule, VesselModule, PartModule

Run when a game load happens. Any data added to the node object in the OnSave method will be available in the node object passed by KSP to this method. Note that any KSPFields are automatically save/loaded and do not require using this method.

 

4) Common Properties/Objects

 

FlightGlobals.ActiveVessel (Flight): A static that is the vessel that currently has focus.

 

Vessel.Parts (Flight): A list of Parts on the vessel.

 

EditorLogic.sortedShipList (Editor): A list of parts placed in the Editor.

 

Part.Modules (Editor/Flight): A list of PartModules attached to this part.

 

PartModule.vessel(Flight): The vessel containing the part this partModule is attached to. This is different than FlightGlobals.ActiveVessel

 

KSPField Attribute. Tag an object with this inside a PartModule, VesselModule, or ScenarioModule and KSP will automatically save and load it for you. Note that only data types of string, bool, int, float, Vector2, Vector3, Vector4 or Quaternion can be tagged this way. Notably this means that the double data type can not be a KSPField.

[KSPField(isPersistant = true, guiActive = false)]
public bool savedBoolean;

This will cause KSP to automatically save/load whether savedBoolean is true or false and it will not be visible in the GUI. If you set one of the GUI booleans to true, the KSPField will show in that part's right click menu (when in a partModule) so the player can edit the value in-game.

KSPEvent Attribute: Only available in a PartModule, this causes a button to show in a Part's right-click menu in editor and flight mode to execute the attached code.

[KSPEvent(guiName ="MyEvent")]
public void MyEvent()
{
	//code to run on player clicking button in part right-click menu
}

The guiName variable is the text that will be visible to the player in-game. There are several other values that can be set as well to control visibility of the Event, but there are too many to list here so check the Object Viewer in your IDE for them all.

KSPAction Attribute: Only available in a PartModule, this adds an action to the action group list for a part in the editor so the player can add it to an action group.

[KSPAction("MyAction")]
public void myAction(KSPActionParam param)
{
	//code to run when the player presses the action group key.
}

The "MyAction" text is what will show in game as the action name. KSPActionParam param is just a fancy boolean passed to the method to indicate if the action group is being activated or deactivated.

5) On-Screen GUI

Legacy GUI system (Obsolete).

Before KSP 1.0/1.1, what is now referred to at the LegacyGUI system was used. This method used the OnGUI() method to draw 2D UI objects on the screen. It is recommended that you not use this system any more and use the current UI Canvas system.

 

UI Canvas system (Current).

Please see this guide: http://forum.kerbalspaceprogram.com/index.php?/topic/151354-unity-ui-creation-tutorial/

UI creation is a complex beast with 3 different ways of doing it and the link covers how to work with it.

 

6) Toolbar button.

 

Squad has a write-up of how to make a button on the stock toolbar here: http://forum.kerbalspaceprogram.com/index.php?/topic/78231-application-launcher-and-mods/

 

Blizzy's toolbar is also popular and instructions for using it can be found here: http://forum.kerbalspaceprogram.com/index.php?/topic/54734-120-toolbar-1713-common-api-for-draggableresizable-buttons-toolbar/

 

 

7) GameEvents system.

 

The GameEvents system is how you trigger parts of your code to run when events in the game happen. All the available GameEvents in the stock game can be found in the GameEvents class in the object browser.

public void Start()
{
	GameEvents.onPartDie.Add(PartDead);
}

public void PartDead(Part p)
{
	//add code here to run when a part dies. 
	//Note that this event fires every time a part dies.
	//You will have to check Part p to see which part died, it is not necessarily the part this piece of code is attached to.
}

public void OnDisable()
{
	GameEvents.onPartDie.Remove(PartDead);
}

Here we subscribe to the GameEvent so that the PartDead method will run anytime a part is destroyed. Note this will run anytime a part dies, so in a big crash this method will run many times, once for each part that dies.

 

Note that we unsubscribe in the OnDisable method. This is very important as if you forget this step you will cause Null-Ref errors as anytime a part dies, KSP will still try to call your PartDead method even though it no longer exists.

 

As of KSP 1.2.2, mods can now trigger game events that other mods can subscribe to for inter-mod communication. Further details on this can be found here: http://forum.kerbalspaceprogram.com/index.php?/topic/153112-ksp-122-gameevents-extension/

 

8) ConfigNode overview.

 

KSP uses ConfigNodes to save/load data. A configNode has the following structure:

NODE
{
	key1 = value1
	key2 = value2
	SUBNODE1
	{
		key3 = value3
		key4 = value4
	}
}

The only valid data type in a ConfigNode is string and is made up of Nodes and Key/Value pairs. The following code makes the above node layout:

ConfigNode myNode = new ConfigNode("NODE");
myNode.AddValue("key1","value1")
myNode.AddValue("key2","value2")
ConfigNode mySubNode1 = new ConfigNode("SUBNODE1")
myNode.AddNode(mySubNode1);
mySubNode1.AddValue("key3","value3")
mySubNode1.AddValue("key3","value3")

Then to retrieve values, the following code extracts them from the node:

ConfigNode myNode = //load ConfigNode here, see section 9, Save/Load data.
string myValue1 = myNode.GetValue("key1");
string myValue2 = myNode.GetValue("key2");
ConfigNode mySubNode1 = myNode.GetNode("SUBNODE1");
string myValue3 = mySubNode1.GetValue("key3");
string myValue4 = mySubNode1.GetValue("key4");

Note that ConfigNodes are very dumb.

If you GetValue('Key5") when Key5 doesn't exist, KSP throws a Null Reference Exception and stop running your code.

If you AddValue("Key1","Value1") when the node already has a Key1 present it will add a second Key1 value to the node instead of updating the existing value.

You can SetValue("Key1","Value1") instead to update an existing Key1 value, but if Key1 doesn't exist, it will Null Reference and stop running your code.

 

When working with ConfigNodes, you can use the "Debug.Log(MyConfigNode.ToString()); command to print the contents of the entire ConfigNode to the log.

 

 

9) Save/Load data.

 

The simplest way to save/load data is to use the KSPField attribute as listed in the Common Properties/Objects section and let KSP do all the work for you. If you needs are more complex you can use the OnSave/OnLoad methods to save to the confignode, or you can directly save to a file on disk.

 

OnSave/OnLoad methods:

Public void OnSave(ConfigNode node)
{
	node.AddValue("myValue","Hello World!")
}

Public void OnLoad(ConfigNode node)
{
	string myValue2 = node.GetValue("myValue");
}

This will save a key/value pair with a key name of myValue with a value of Hello World! and then when the load happens, myValue2 will have a value of Hello World loaded into it. See the ConfigNode section for working with ConfigNodes.

 

Save/Load directly to disk:

ConfigNode myNode = ConfigNode.Load(KSPUtil.ApplicationRootPath + "GameData/MyModFolder/MyMod.settings"); 
//Loads a file from disk into a ConfigNode for use in code.

ConfigNode myNode.Save(KSPUtil.ApplicationRootPath + "GameData/MyModFolder/MyMod.settings");
//Saves a confignode to disk.

This will save/load to the MyMod.settings file in the MyModFolder. The file structure is a ConfigNode so see that section on working with them.

 

 

And those are the absolute basics of how to start modding for KSP. If you spot any discrepancies or basics that were missed that should be mentioned please let me know.

Apr 12/17: Update VesselModules now run on unloaded vessels. (New in KSP 1.2)

Edited by Diazo
  • Like 22

Share this post


Link to post
Share on other sites
On 12/20/2016 at 11:49 AM, Diazo said:

When you start KSP and reach the main menu, if everything is setup correctly, the code above will spam "Hello world!" to the console which is toggled on with Alt-F12. It will also append the time since the game was started so you can see the lines of text change.

This is slightly misleading, because

9 minutes ago, nightingale said:

@Aghanim - This line:


	[KSPAddon(KSPAddon.Startup.Flight, false)]

Means that your addon will be started in the flight scene.  You'll see your log spam if you launch a vessel.

and that will probably leads people (like me) thinking that there might be something wrong with their code or setup because they checked the console at main menu and nothing happens, when in fact the plugin is fine, it just fires at flight scene not at main menu

 

  • Like 1

Share this post


Link to post
Share on other sites

Thanks for catching that, fixed.

One of these days I'll stop making copy-paste errors. I grabbed some of my own code that runs in the flight scene for an example and that slipped through.

D.

  • Like 1

Share this post


Link to post
Share on other sites

Nice overview

You mentioned KSPFields with VesselModules, from what I have read they do not save any data, something worth mentioning (unless something changed recently)

  • Part Module: One set of modules per part. Number of modules for a part is set in part configuration file. Active during initial loading (do try not to...), editor, and flight scenes. Save persistence of variables using the KSPField attribute with the option isPersistant set true 
  • KSP Addon: Single instance. Only one instance of the plugin object running, limited to either a single scene or all scenes 
  • Scenario Module: Single instance per save, used for storing/retrieving persistent data that isn't associated with a part (? I haven't used this one so I don't know much about it) 
  • Vessel Module: One instance per physics enabled vessel object. Only active in the flight scene. No direct access to the persistence file. 

 

  • Like 1

Share this post


Link to post
Share on other sites

@gomker The linked thread is from when VesselModules were originally implemented in KSP 1.0 and at the time they did not save data.

In KSP 1.1 (or 1.1.1?) VesselModules gained the ability to save/load data via the standard OnSave()/OnLoad() methods that PartModules have. I'm pretty sure this included the KSPField for save/loading data from what I recall at that time but I will admit I didn't actually that feature when I was writing the guide.

I'll look into that in the next few days here.

D.

Share this post


Link to post
Share on other sites
7 hours ago, gomker said:

Vessel Module: One instance per physics enabled vessel object. Only active in the flight scene. No direct access to the persistence file. 

Err, Load and Save calls were added in 1.2 so that s no longer the case.

Edit : and I should learn to read replies :)

 

Edited by sarbian

Share this post


Link to post
Share on other sites
On 12/20/2016 at 5:49 AM, Diazo said:

 

KSPAddon

 ... the KSPAddon.Startup enum and multiple values to start in multiple scenes can be selected.

The scenes have ordinal values like Editor= 6, Flight= 7, TrackingStation= 8, and so on. There are also some predefined combinations like FlightAndEditor= -4. Is there a way to select multiple values other than picking one of the predefined combinations ?

Share this post


Link to post
Share on other sites
13 minutes ago, Rodhern said:

The scenes have ordinal values like Editor= 6, Flight= 7, TrackingStation= 8, and so on. There are also some predefined combinations like FlightAndEditor= -4. Is there a way to select multiple values other than picking one of the predefined combinations ?

No; the pattern I've seen for multiple scenes is to inherit several scene-specific classes from your main plugin class, one per attribute.

https://github.com/TriggerAu/TransferWindowPlanner/blob/master/TransferWindowPlanner/TWP.cs#L16-L25

  • Like 1

Share this post


Link to post
Share on other sites
On 12/19/2016 at 10:49 PM, Diazo said:

These files are found in the “KSP_Install\KSP_x64_Data\Managed”.

I am brand new and confused to modding! Also to C#, and Visual Studio, and actually programming. I'm basically insane.

But I did want to share that in my install, KSP 1.3.0 Linux (Lubuntu 16.04, if it matters) installed through Steam, the files are in "Kerbal Space Program/Launcher_Data/Managed". There is no "KSP_Install\KSP_x64_Data\Managed" as there's only a "Mono" dir in my "KSP_x64_Data".

As the OP was last edited 8/9 months ago, I'm presuming this is more a version update thing than a Linux/Windows thing, but I wouldn't know. Potentially this will help someone though!

Share this post


Link to post
Share on other sites
28 minutes ago, Benjamin Kerman said:

I know you use them in KSPFields, but what else are they used for?

Attributes are more or less used to give KSP information about your mod (dotnet classes). One that is often used is KSPAddon, e.g.

[ KSPAddon (KSPAddon.Startup.TrackingStation, false) ]

that way KSP knows that you want that class 'created'/'loaded'/'started' every time the player goes to the tracking station, and that the class can 'unload' when the player exits the tracking station.

The attributes are discovered by KSP/Unity/Mono by something called "reflection". Something about Unity can make it quite a bit more confusing than it ought to be. Some classes are found by reflection due to their inheritance, some methods are identified by reflection due to the name, and some are found by reflection due to their attributes (e.g. KSPField and KSPAddon).

Look at the first few posts of this thread - there are good explanations there.

  • Like 1

Share this post


Link to post
Share on other sites
2 minutes ago, Benjamin Kerman said:

So they take the form of enums?

Are you asking about attributes? Attributes inherit from System.Attribute (as described in the link you gave above), and are just classes. The difference is that they are often used in a bit of a different way from other classes.

  • Like 1

Share this post


Link to post
Share on other sites

I dont quite understand how to write an addon that gets created once and then persists through the entire game.

I have tried "[KSPAddon(KSPAddon.Startup.MainMenu, true)]", which, as far as i understand should be started once the main menu loads and never be destroyed. However it gets destroyed once the scene changes (eg i load a save).

Share this post


Link to post
Share on other sites

@c4ooo You have to specifically tell it not to be destroyed. 

private void Awake()
{
	DontDestroyOnLoad(gameObject);
}

Put that in the MonoBehaviour you are creating and it won't be destroyed unless you do it manually.

  • Like 1

Share this post


Link to post
Share on other sites
2 hours ago, DMagic said:

@c4ooo You have to specifically tell it not to be destroyed. 


private void Awake()
{
	DontDestroyOnLoad(gameObject);
}

Put that in the MonoBehaviour you are creating and it won't be destroyed unless you do it manually.

Thanks, that seemed to have worked :)

Should i leave the Dont_Destroy boolean true or false?

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now