Sign in to follow this  
Majiir

Part Classes and a Robust API

Recommended Posts

I toyed around with the part class loader last night. (My proud creation: a solid booster that explodes when you pause/resume the game.) There\'s obviously a lot of potential here, since we\'re essentially working with the same tools as the developers. There\'s just something bugging me about the API we\'re given: it\'s messy.

I want to discuss how we (the KSP community) might develop an API for part modding that meets several goals:


  • [li]Takes full advantage of language features (like events) and complies with common C# coding conventions (like PascalCase member names)[/li]
    [li]Provides a clean interface with everything we need and nothing we don\'t[/li]
    [li]Is useful in intermediate stages of development, not just when finished[/li]
    [li]Requires minimal modification to KSP itself[/li]

This is no small task, so I thought some brainstorming and theory-crafting might be in order before jumping on in.

Share this post


Link to post
Share on other sites

We would basically have to encapsulate the whole game, or big portions of it. Too add to the problem, the game is constantly changing, and I\'ve already had to make changes to my modules twice since x1 came out.

The idea is interesting, and a class that brings useful functions together would be useful, but maintaining it with the game basically in a alpha state would be pretty hard.

Still, if someone has any idea that could make this work, I think it may be worth the trouble.

Share this post


Link to post
Share on other sites

That\'s an accurate criticism, but there\'s a flipside:

I\'ve already had to make changes to my modules twice since x1 came out.

While difficult to maintain, a well-structured API can provide stability for plugin writers.

Share this post


Link to post
Share on other sites

There\'s just something bugging me about the API we\'re given

What API? I\'ve been looking for any kind of information about, for example, what are the methods and variables of the part class, and I\'m not finding anything that looks anything like an API? Does something like that exist and I haven\'t found it?

Share this post


Link to post
Share on other sites

All information regarding classes and their functions are stored within the .dll, in this case 'Assembly-CSharp.dll' dounf in KSP_Data. All you need to do is add a reference to this dll in visual studio or w/e ide you are using.

getOrbitalVelocityAt() returns double, 'velocity' implies direction (in this case should be a 3d vector)

The little things you know?

Share this post


Link to post
Share on other sites

Oh, I see. I\'ve never worked with dll\'s nor c++ before, and I\'m used to doing all my coding in a text editor. So I\'m going to have to break down and use an IDE, huh?

Thanks for the info.

Share this post


Link to post
Share on other sites

Oh, I see. I\'ve never worked with dll\'s nor c++ before, and I\'m used to doing all my coding in a text editor. So I\'m going to have to break down and use an IDE, huh?

Thanks for the info.

You don\'t have to, but it\'ll be about a thousand times easier since there\'s no online documentation.

Share this post


Link to post
Share on other sites

I\'ve started on a primitive API as a proof of concept. Here\'s the source for the test plugin I made:

using System;
using KerbalAPI;

// This is the class in the part.cfg file
public class SabotageProxy : Sabotage.Proxy { }

// This is where we put our code
public class Sabotage : Part<Sabotage>
{
public Sabotage()
{
ActiveUpdate += MyUpdateHandler;
}

private void MyUpdateHandler(object sender, EventArgs e)
{
Explode();
}
}

On the surface, this might not look any better (it might even look worse) than the equivalent plugin using the standard API. There are a few important differences, though, and this method has a great deal of potential. Note that the Sabotage class does not extend the KSP Part class. The generic Part<> class it extends is part of KerbalAPI and defines a clean interface without any Unity methods. ActiveUpdate is a C# event, and you can see it handled by MyUpdateHandler(). Explode() works just as KSP Part.explode() does, but other methods (like requesting fuel) might be improved by an API implementation.

This is achieved so far without reflection. I\'d like to avoid using reflection wherever possible, though in some cases it might be appropriate. (For example, to expose public properties to the part.cfg file.)

When I get home I\'ll put the source on Github and post a link here.

[EDIT] Source is here: https://github.com/KerbalAPI/KerbalAPI

[EDIT] I should note that this is an equally valid way to handle the event:

protected override void OnActiveUpdate()
{
base.OnActiveUpdate();
Explode();
}

The difference is basically just capitalization for now, but the takeaway is that more complex functionality can be handled by the API while exposing a useful interface for plugin developers.

[EDIT] This also lets us use namespaces! ;D

Share this post


Link to post
Share on other sites

I spent some time last night thinking about an interesting design problem.

Ideally, a more complete API will proxy/wrap a whole collection of KSP classes. The most important is probably the Part class. My wrapper class exposes C# events, which is certainly handy for developing a single part type. Where it really shines is when you subscribe to events from other parts, ones that you aren\'t necessarily writing a plugin for. (Suppose you want to activate a decoupler when a stage runs out of fuel; you can subscribe to the child part\'s events and listen for that state without actually modifying any fuel tanks.) So, being able to wrap any arbitrary Part is important.

Here\'s the problem: KSP doesn\'t expose events. In order to make this work, parts of the underlying Part class (specifically, the event methods) need to be modified in a similar way as my Proxy class. We can\'t decompile and redistribute the KSP binaries, so that leaves us with the Dark Art of programming: reflection.

I\'m admittedly a reflection newbie, but I think it may be possible to build a loader which patches the KSP classes in memory before running the game. It\'s a bit more than I wanted to do, but I think the payoff may be worth it. :)

Share this post


Link to post
Share on other sites

Majiir,

Something like this might work. (I have not tested any of this, but its a standard Observer pattern scenario)

UPDATE: Upon review this isn\'t quite right. I need time to think about it outside of work hours. Its close. Sorta.

public interface IKSPPartCallback

{

void PartActivated(Part partThatWasActivated);

void PartDestroyed(Part partThatWasDestroyed);

}

public class KSPPart : Part

{

static List<IKSPPartCallback> _callbacklist = new List<IKSPPartCallback>();

protected void Subscribe(IKSPPartCallback subscriber) { // add to list };

protected void Unsubscribe(IKSPPartCallback subscriber) { // remove from list };

protected override onPartActivate()

{

foreach (IKSPPartCallback part in _callbacklist)

part.PartActivated();

}

protected override onPartDestroyed()

{

foreach (IKSPPartCallback part in _callbacklist)

part.PartDestroyed();

}

}

public class MyPart : KSPPart, IKSPPartCallback

{

public void PartActivated(Part partThatWasActivated)

{

// check if its the part you are watching

}

public void PartDestroyed(Part partThatWasDestroyed)

{

// check if its the part you are watching

}

}

Share this post


Link to post
Share on other sites

The problem with that example is that MyPart extends KSPPart, which in turn extends the global Part class provided by KSP. I think piling additional methods on top of that class would create unnecessary confusion.

I\'ve been a bit slow to make progress here since I\'ve been toying around with reflection, but I have a revised plan for wrapping KSP classes. Each part has up to one wrapper object, which in turn wraps exactly one KSP part object. The wrapping mechanism is completely hidden from the (code) user. When an API Part object is created, the corresponding KSP (and Unity) object is created as well. When any API function returns a Part, it returns a wrapper, creating and binding it if it doesn\'t already exist.

If I can get over the reflection hurdle, I think the advantages will be obvious. I might be very busy before April 1, though, so we\'ll see how much time I can put into it...

Share this post


Link to post
Share on other sites
This thread is quite old. Please consider starting a new thread rather than reviving this one.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this