TriggerAu

KSP Plugin Framework - Plugin Examples and Structure v1.1 (Apr 6)

Recommended Posts

As some of you might know I have been working on a rewrite of my Kerbal Alarm Clock and the KSP Alternate Resource Panel. In doing this I got to the point of creating a common code base for generating a plugin. I have been recording my progress in my KSP Blog - which I at least think is entertaining.

Anyway's the point of this post was to let people who haven't noticed the blog that I have published the basic framework I am using going forward with the source and some examples. Basically you can take this framework and with a few additional lines of code have a window displaying data in a plugin in KSP. Alternatively you can look at the examples and take whatever you need to get some ideas/working code.

The Source and Examples can be found here

Am am pretty much finished with the current iteration which includes:

  • MonoBehaviourExtended - adds some bits to the base MonoBehaviour class - logging, repeating background function, definitions of Unity Events
  • MonoBehaviourWindow - Adds to the above with all the bits to draw a window, Contains code for - Visibilty, Screen Clamping, Tooltips, Wrappers
  • SkinsLibrary - A way to change the whole guis skin in one go for your code as well as easily set up new skins.
  • ConfigNodeStorage-This class (and docco) helps with being able to have a "settings" object that you can easily save/load as required, and also some examples of how ot serialise complex objects
  • Some other helpers

All the classes contain as much commenting as I could fit in there so the ObjectBrowser/Intellisense includes info to let you know what they do, as well as all the info in the Blog posts and the example code. Also of note may be the tips I pulled together in post 3 about speeding up plugin testing. Here's the full list:

Entries in this Series

I'll continue to tweak this as I get the rewritten plugins in place and improve, and as always love feedback and ideas to help improve it.

EDIT: Forgot to mention this in the post before (but it is in the blog posts) I set the license as MIT, which I believe means its anyones to do what they like with it. That was the intent of the framework anyway so I hope theirs no confusion

Edited by TriggerAu
Updated Source Code Location

Share this post


Link to post
Share on other sites

ConfigNodeStorage Inherits from ConfigNode, so it has all that goodness, but adds the following Methods/features:

  • Storage of a file name in the object and a constructor so when you create the object you can give it a file to interact with... this can be via relative or absolute paths
  • It implements the IPersistenceLoad and IPersistenceSave Interfaces for custom encode/decode, and gives you overides you can use if needed
  • Has Save and Load functions to pass the object in and out of the file
  • It also has AsConfigNode property so you can access the objects full ConfigNode

What does this all mean? Less coding for you and me to write when you want to read/write ConfigNode stuff, so this code

    [KSPAddon(KSPAddon.Startup.MainMenu,false)]
class ConfigNodeExample :MonoBehaviourExtended
{
SettingsCN settings = new SettingsCN();

internal override void Awake()
{
String FilePath = System.IO.Path.Combine(
System.Reflection.Assembly.GetExecutingAssembly().Location,
"Settings.cfg").Replace("\\", "/");

ConfigNode cnLoad = new ConfigNode();
cnLoad = ConfigNode.Load(FilePath);
ConfigNode.LoadObjectFromConfig(settings, cnLoad);

settings.TestString = "Hello again";

ConfigNode cnToPrint = new ConfigNode("settings");
cnToPrint= ConfigNode.CreateConfigFromObject(settings);
LogFormatted(cnToPrint.ToString());

cnToPrint.Save(FilePath);
}
}

class SettingsCN:IPersistenceLoad,IPersistenceSave
{
[Persistent] internal String TestString = "Fred";
[Persistent] internal Boolean TestBoolean = true;
}

Can become

    [KSPAddon(KSPAddon.Startup.MainMenu, false)]
class ConfigNodeStorageExample : MonoBehaviourExtended
{
SettingsCNS settings = new SettingsCNS("Settings2.cfg");
internal override void Awake()
{
settings.Load();
settings.TestString = "hello again";
LogFormatted(settings.AsConfigNode.ToString());
settings.Save();
}
}

class SettingsCNS:ConfigNodeStorage
{
public SettingsCNS(String FilePath) : base(FilePath) { }
[Persistent] internal String TestString = "Fred";
[Persistent] internal Boolean TestBoolean = true;
}

Basically, this - and the other classes in the framework - are boilerplate code that should save people time in plugin creation. A lot of the stuff that we all have to write every time is in here and you can just use this code instead of writing/copying the code each time.

EDIT: For all the classes there are Example projects in the Source you can look at too

Share this post


Link to post
Share on other sites

I've only just started looking at this, but it looks pretty cool so far :) I have just gotten to the stage with my plugin that I need to write some sort of GUI for it, so your timing is perfect.

I have a couple of things you might want to consider adding to your to-do list:

- The classes aren't in a namespace and everything is internal rather than public. I don't really mind and I only noticed because I initially put them all in a separate project to keep them separate, but if you are bored one day :).

- It might be worth wrapping the gameObject.AddComponent<> call into MonoBehaviourExtended, just to make it a bit neater and easier to remember. Something like AddChild<T>()?

- Integration with blizzy78's toolbar would be cool. Perhaps a call in MonoBehaviourExtended that adds a toolbar button and sets it up to change the visibility of a child Window when clicked?

Share this post


Link to post
Share on other sites
I've only just started looking at this, but it looks pretty cool so far :) I have just gotten to the stage with my plugin that I need to write some sort of GUI for it, so your timing is perfect.

I have a couple of things you might want to consider adding to your to-do list:

- The classes aren't in a namespace and everything is internal rather than public. I don't really mind and I only noticed because I initially put them all in a separate project to keep them separate, but if you are bored one day :).

- It might be worth wrapping the gameObject.AddComponent<> call into MonoBehaviourExtended, just to make it a bit neater and easier to remember. Something like AddChild<T>()?

- Integration with blizzy78's toolbar would be cool. Perhaps a call in MonoBehaviourExtended that adds a toolbar button and sets it up to change the visibility of a child Window when clicked?

Thanks for this Thalur, re the points

- I didnt use namespaces so that the code would inherit the projects namespace, but then again I should just use a namespace and people can use "using" hey :)

- I made it all mostly internal as these objects wouldn't be referenced outside the plugin assembly (I didnt see people using this as an extra dll in their plugin, just including the code classes in their dll), or at least that was my thinking I'm still not quite 100% on when I should be using public and when internal.

- Some form of factory method for that initialisation was on my list, but I struggled with understanding Generic Methods, I need to get a grip on that to implement it, and its a great idea thanks

- Yeah toolbar integration thats an awesome one, and it didnt even cross my mind *headslap*. I need to update the wrapper code I use in KSP ARP for the toolbar, so I'll bring that in here too

Thanks again, this is great

Share this post


Link to post
Share on other sites

funny... looking in the code now I already wrote this and commented it out

internal static MonoBehaviourExtended CreateComponent(GameObject AttachTo)
{
MonoBehaviourExtended monoReturn;
monoReturn = AttachTo.AddComponent<MonoBehaviourExtended>();
return monoReturn;
}

If I remove the static and figure out the generic bit that would give us the AddChild function I think

Share this post


Link to post
Share on other sites

public T AddChild<T>() {
return gameObject.AddComponent<T>();
}

should do it. You could limit the generic type to a valid type but apparently Unity doesn't even do that (and instead just throws a runtime exception). Definitely take a look at generics, they're not as complicated as they might seem.

As for the entire thing: I haven't looked at it, but I always think it's great if someone does the work to try to offer the community part of his work for reuse. Thumbs up!

Edited by Airblader

Share this post


Link to post
Share on other sites

Thanks Airblader, I got this generic working...

    internal T AddChild<T>() where T:UnityEngine.Component
{
return gameObject.AddComponent<T>();
}

So now the call in the main code is

MonoBehaviour NewWindow = this.AddChild<MonoBehaviourWindow>()

but looking at the code all I've done is remove the gameObject text from the original version, and added another function.

MonoBehaviour NewWindow = this.gameObject.AddComponent<MonoBehaviourWindow>()

I'm sure I'm missing something here, or is it just the fact that the AddChild idea is easier to read/autotype?

Share this post


Link to post
Share on other sites

Yes, it only introduced a wrapper method, getting rid of using "gameObject". Like Thalur said, it's only a bit of cosmetics. ;) You can omit the "this", by the way.

Share this post


Link to post
Share on other sites

Thanks for this, I used it in my plugin.

Just a note that there's a compilation warning in the SkinsLibrary:

if (lstTemp.Any(x => x.name == NewStyle.name))
{
GUIStyle styleTemp = lstTemp.First(x => x.name == NewStyle.name);
styleTemp = NewStyle;
}

Because styleTemp is assigned to, but never used. Something screwy going on there.

Share this post


Link to post
Share on other sites
Thanks for this, I used it in my plugin.

Just a note that there's a compilation warning in the SkinsLibrary:

if (lstTemp.Any(x => x.name == NewStyle.name))
{
GUIStyle styleTemp = lstTemp.First(x => x.name == NewStyle.name);
styleTemp = NewStyle;
}

Because styleTemp is assigned to, but never used. Something screwy going on there.

Good point.

I have made some edits in the ARP code as I used it myself, I'll check for stuff that can come back to the core framework and upload a couple of tweaks.

Thanks

Share this post


Link to post
Share on other sites

An update to the framework

So I've gone through what I had tweaked in using the framework in the ARP and uploaded any useful changes to it.

There were a few including the code section that Unit327 highlighted. You can read the changes here An Adventure in Plugin Coding - 8. Revisiting the Scene of the Crime

If your using the framework you can simply overwrite the existing code files if you want the changes. You may need to add "using KSPPluginFramework;" to your code files as I have added a default namespace

Share this post


Link to post
Share on other sites

Thanks again. I'm using the framework in a separate assemblies, as a reference. Because of that I had to edit all the "internal" modifiers on everything as they were a bit too strict. Otherwise thanks for making my life that little bit easier!

Share this post


Link to post
Share on other sites

I've just updated the code in the blog series for the KSPAddon that kicks you into a save game and into a vessel. I edited my code a little while ago, but forgot to update this stuff till I saw this post - http://forum.kerbalspaceprogram.com/threads/78002-0-23-5-updated-auto-load-on-startup-code

Anyways. Heres a blog post about the updated code that handles asteroids for you in the auto load addon - An Adventure in Plugin Coding - 3. A Fast Dev Environment... I hope

Share this post


Link to post
Share on other sites

I need some help to actually get started with your framework. Something noobish, from a modder perspective.

Though I opened and recompiled a number of KSP mods, I never found code with the "using KSP;" statement (just references to MS stuff and UnityEngine). But you use it plenty, and I can't find how to reference that namespace (I even searched for a KSP.dll with the game files - what a noob here). I am sure it is an absolute must to use KSP functions with any mod.

Searching threads, i could only find this (Anatid's KSP API documentation) that really seems tied to that. Should I compile those in a library named KSP.dll?

And, of course, found KSP.IO references in KSP Wiki (and some code I see actually use that) but that doesn't seem the right one.

Share this post


Link to post
Share on other sites
You need to add references to KSP_Data/Managed/Assembly-CSharp.dll and same/UnityEngine.dll

Many many thanks. Never realized that KSP namespace could be embedded in another dll (both UnityEngine and Assembly-CSharp I linked when called for, without knowing).

Share this post


Link to post
Share on other sites

In reference to the the 'using KSP', the KSP functions are in the global namespace, which is why there is no 'using KSP'. All the 'using' statement does is to make it so that you don't have to type out the full name of functions. For example UnityEngine has a Vector3 class. If you had 'using UnityEngine;' you just type 'Vector3' to use the class, but you can still use it without the 'using UnityEngine', you would just have to type out 'UnityEngine.Vector3' instead.

Share this post


Link to post
Share on other sites

@ Xty: yes, that makes sense. Indeed the short form of functions, as they are written in the framework, is not understood by a compiler unless the namespace is referenced (as suggested by Nathankell, that worked). And not only functions, but all properties of objects defined in that namespace.

Share this post


Link to post
Share on other sites

Great to see people chatting their way through problems. I love it. Yeah I use "Using" to save me some keystrokes.

Share this post


Link to post
Share on other sites

I'm (almost) clueless when it comes to C# and IDEs (all my experience is with interpreted languages, mostly Python), but I do know that having a framework that does all the little stuff you're going to do for every freaking bit of code you write is...useful to say the least.

Good job. If I ever motivate myself into actually writing a KSP plugin, I'll be starting here, that's for sure.

Share this post


Link to post
Share on other sites

Curious if you plan to add the MonoBehaviourWindowPlus functionality from Transfer Window Planner to this? The drop-down is awesome. I'm going to pick that up for a current project along with the ConfigNodeStorage class. Also, thanks so much for MIT'ing this project!

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.