-
Notifications
You must be signed in to change notification settings - Fork 32
Interop System
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?
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.
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.
- Names must be unique across all mods.
- 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.
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.
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:
- Upon Start() after setting up the property internally for the Flight or Editor scenes.
- 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 });
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 });
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 });