Skip to content
jwvanderbeck edited this page Feb 26, 2015 · 4 revisions

TestFlight works by tracking individual parts, and as such it has to have a way to tell each part from another. In stock KSP this is easy, because every part has a unique name, and the configuration of the part does not change during runtime. A Mainsail is the same no matter what, so is Jumbo 32 fuel tank.

However many awesome mods such as RealFuels and Procedural Parts introduce the concept of parts that do change configuration during runtime. Is a Procedural Parts fuel tank 1m long? 5m? What is its diameter? It changes. Treating every sized procedural tank as one part is bad, and doesn't allow for things such as larger tanks being less reliable if the mod author so desires. One of TestFlight's core concepts is a high level of configuration ability.

How do we solve this problem, and do it in a clean mod independent way?

Interops

TestFlight is introducing an Interoperability system called Interop. This is a generic mod agnostic interface that allows any mod that implements dynamic parts to essentially tell TestFlight what those dynamic properties are and what their current values are.

An extremely simple reflection interface, designed to be as easy to use by the mod authors as possible, removes the need for mod specific code inside of TestFlight while still allowing TestFlight to respond to and work with dynamic part properties.

Interop Values

The interface works on a concept of Interop Values. An interop value is one distinct property that the mod feels would be useful to use as a method of uniquely identifying and configuring a part inside of TestFlight. For example RealFuels might add an interop value for tank type, and one for engine configuration. Procedural Parts might add interops for tank diameter and height, or whatever else a mod feels would be useful.

Each interop value is identified by a name which the mod itself chooses. Three things should be kept in mind when deciding on names.

  1. Names must be unique across all mods.
  2. Names should be short, and easily referenced by users in .cfg files

To facilitate point #1 I encourage any mod author that implements Interops to let me know what interop value names they use, so that I can add them to the Wiki page that lists them all for reference.

Interop Interface

The Interop Interface has been written specifically to be extremely easy to use. It makes use of simple static methods than can be called through reflection in one line of code. The only other requirement is simply verifying the existence of TestFlight first. Optionally, though recommended, you can wrap the call in a try/catch block for safety.

In addition to the interop value name a mod also passes in a string that identifies itself as the owner of that interop value. This is to prevent accidental collision by multiple mods that inadvertently try to use the same name. I recommend simply using the name of your mod, or the PartModule as your identifying name.

There are three simple methods in the interface. You can add a new value, or update an existing one. You can remove a value. And you can clear all values belonging to your mod.

Checking if TestFlight is installed

Before using the Interop interface it is best to check to see if TestFlight is even installed. You can simply cache this value.

Type tfInterface = null;
if (tfInterface == null)
    tfInterface = Type.GetType("TestFlightCore.TestFlightInterface, TestFlightCore", false);

Simply cache the tfInterface variable, and if its not null you can use it to call the Interop system. AddInteropValue

public static bool AddInteropValue(Part part, string name, string value, string owner)

  • part is the KSP part in question
  • name is the Interop Name, IE the name used by .cfg files to reference this interop value.
  • value is the value of the Interop. The signature above shows it as a String but overloaded methods accept the value as: string, float, int, bool.
  • owner is a string identifying the owning mod. This should be either the name of the mod or the name of the PartModule, it is up to you but it should be considered case sensitive.

Return Value: Returns true if the value was added successfully. false if not. Generally a return value of false indicates that the name you tried to use has already been used by another owner, or your owner string is incorrect and not matching with a previous addition you made.

Call this method to add a new value or to update one previously added with a new value. Generally this will be called in one of two situations:

  1. Upon Start() after setting up the property internally for the Flight or Editor scenes.
  2. The user or internal processes have done something to alter the value and it needs to be updated in TestFlight.

NOTE: Please use with caution, especially point #2 above. Remember that these values are being used to map the current configuration of your part to a persistent data store for that part. Generally a part's configuration should not change during flight, because if it does it will then be identified internally by TestFlight as a completely different part. Changes in the Editor make sense, but during Flight often does not. Now this is a guideline not a rule, as its possible there might be a situation where changes in Flight make sense and it should be treated as a new part, but just keep that in mind!

Example Call:

bool valueAdded = (bool)tfInterface.InvokeMember("AddInteropValue", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new System.Object[] { part, name, value, owner });

RemoveInteropValue

public static bool RemoveInteropValue(Part part, string name, string owner)

  • part is the KSP part in question
  • name is the Interop Name, IE the name used by .cfg files to reference this interop value.
  • owner is a string identifying the owning mod. This should be either the name of the mod or the name of the PartModule, it is up to you but it should be considered case sensitive.

Return Value: Returns true if the value was removed successfully. false if not. Generally a return value of false indicates that the value you tried to remove is owned by someone else, or your owner string is incorrect and not matching with a previous addition you made.

This will completely remove the interop value from TestFlight and it will no longer be available for users to query.

Example Call:

bool valueRemoved = (bool)tfInterface.InvokeMember("RemoveInteropValue", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new System.Object[] { part, name, owner });

ClearInteropValues

public static void ClearInteropValues(Part part, string owner)

This method is simply a convenience wrapper to calling RemoveInteropValue on all values added by a given owner.

Example Call:

bool valueRemoved = (bool)tfInterface.InvokeMember("ClearInteropValues", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new System.Object[] { part, owner });