Sign in to follow this  
lo-fi

PartModules, submodules and class extensions

Recommended Posts

Hi

I've been pondering how some of the stock modules are set out, and hoping someone can throw some light on it for me. To explain:

ModuleWheel is called in the part.cfg like any other PartModule:


MODULE
{
name = ModuleWheel
}

But, it has what I assume is an extension called Wheel, which is called from within the PartModule parenthesis:


MODULE
{
name = ModuleWheel

WHEEL
{
//some stuff specific to Wheel here
}

}

You can call many instances of Wheel from within ModuleWheel. So my question is, how is this setup in C#? I had assumed that Wheel is simply a class extension to ModuleWheel, but trying to replicate that myself doesn't appear to work. For example:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace KerbalFoundries
{
[KSPModule("ClassTest")]
class ClassTest : PartModule
{
public float testFloat;
public override void OnStart(PartModule.StartState state)
{
base.OnStart(state);
if (HighLogic.LoadedSceneIsFlight)
testFloat = 2.55f;
print("ClassTest called");
}
}
}

This works absolutely fine when called in a .cfg as normal:



MODULE
{
name = ClassTest
}

I also have a class extension:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;


namespace KerbalFoundries
{
class ClassExtensionTest : ClassTest
{
[KSPField]
float extensionFloat;


public override void OnStart(PartModule.StartState state)
{
base.OnStart(state);
if (HighLogic.LoadedSceneIsFlight)
{
print("ClassExtensionTest called");
print(testFloat);
print(extensionFloat);
}
}
}
}

Now, assuming that's the right way to set it up (and that's a big assumption), how do you call it from the config from within ClassTest? I assume the capitalisation of WHEEL in the part.cfg is significant, but I'm lost from here. Just a pointer to a mod with source that's got it figured out would be gratefully received, but if someone can explain more fully that would be brilliant.

Many thanks for your continued help for a programming newbie.

Lo-Fi

Share this post


Link to post
Share on other sites

Okay, extending a partModule works slightly differently then you are thinking.


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace KerbalFoundries
{
//[KSPModule("ClassTest")] commented this line out, I don't use it in my own mods so I'm not sure what it does.
public class ClassTest : PartModule //made the module public, not sure it is necessary but it makes sure calls from other places work fine
{
public float testFloat;
public override void OnStart(PartModule.StartState state)
{
base.OnStart(state);
if (HighLogic.LoadedSceneIsFlight)
testFloat = 2.55f;
print("ClassTest called");
}
}

public class ClassTestExtension : ClassTest //made the module public, not sure it is necessary but it makes sure calls from other places work fine
{
public float testFloat;
public override void OnStart(PartModule.StartState state)
{
base.OnStart(state);
if (HighLogic.LoadedSceneIsFlight)
testFloat = 2.55f;
print("ClassTestExtension called");
}
}
}

This code will call both ClassTest and ClassTestExtension as follows


MODULE
{
name = ClassTest
}


MODULE
{
name = ClassTestExtension
}

Note they are on the same 'level' in the part.cfg. Anything that extends PartModule, no matter how many layers deep, is referred to in the part.cfg this way.

The WHEEL in ModuleWheel that you see is a sub-node.

If you have a part.cfg like so


MODULE
{
name = ClassTest
SUBNODE
{
name = SubNode
}
}

You can refer to it as follows:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace KerbalFoundries
{
//[KSPModule("ClassTest")] commented this line out, I don't use it in my own mods so I'm not sure what it does.
public class ClassTest : PartModule //made the module public, not sure it is necessary but it makes sure calls from other places work fine
{
public float testFloat;
public SubNode ourTestNode
public override void OnStart(PartModule.StartState state)
{
base.OnStart(state);
if (HighLogic.LoadedSceneIsFlight)
testFloat = 2.55f;
print("ClassTest called");
ourTestNode = new SubNode(); //define your SubNode class as normal, this example assumes it has a "public string name;" field defined.
}
//how do I tab over in the forum edit box anyway?
public override void OnLoad(ConfigNode node)
{
ConfigNode subNodeLoad = node.GetNode("SUBNODE"); //this example assumes a single SUBNODE, if you have (or may have) multiple SUBNODE, you use node.GetNodes to return a list of SUBNODE.
ourTestNode.name = node.GetValue("name"); //
print("SUBNODE loaded named: " + ourTestNode.name);
}

}

Hope that clarifies things.

D.

Share this post


Link to post
Share on other sites

Ah, thanks for the detailed explanation. Gives me what I need to continue reading up, I just couldn't work out what I was looking at. Am I missing a reference, though, or did you mean ConfigNode rather than SubNode in the code?

Also, are there performance advantages of either method? By which I mean calling as a partmodule several times, rather than as nodes within a partmodule. Or is this really for programmatical convenience?

Newbie programmer, sorry for what may seem like silly questions.

Thanks again

Share this post


Link to post
Share on other sites

Anything inside a MODULE is simply data storage for that PartModule. If it has nodes as well as values, that just means that PartModule is set up to expect and parse certain nodes as well. For example, if there's a KSPField that's a float curve, the way data is serialized for it is as a node (of the name of the float curve, like atmosphereCurve) and inside that a series of "key" values. Probably ModuleWheel has a list of wheels which it reads based off getting all WHEEL nodes in its confignode and parsing each. Same as how ModuleEngines has a List<Propellant> propellants, which is filled by finding and parsing all PROPELLANT nodes inside the MODULE node for it.

tl;dr you're not "calling a class" with *anything* you put inside a MODULE node; *all* that's inside the node is merely serialized data for the PartModule. If the PartModule doesn't know what to do with the nodes and values, they will have no effect.

Share this post


Link to post
Share on other sites

So PartModules don't have a mechanism to parse and serialise the data for such an entry a .cfg in the same way as a KSPField, I would have to write that into my partmodule myself(?) Thanks, that clears things up even further. I have a working method calling partmodules that grab variables from each other, I just wondered if the 'stock' way (if you want to call it that) would be easier or more efficient and I was missing an obvious feature.

Thanks again.

Share this post


Link to post
Share on other sites

All a KSPField is, is this:

When a PartModule is created from cfg, all KSPField-tagged members will be set based on parsing the cfg (where simple types are just parsed, FloatCurves have Load(floatcurvenode) called, etc ) and the OnLoad method is called, where you can handle custom parsing (i.e. find all WHEEL sub-nodes in your node and do stuff with them).

If the data type you are trying to read is not handled by default by stock code, then you will have to parse it yourself in OnLoad(). For instance, if it's a double. KSP doesn't parse doubles itself (KSPField doesn't work for doubles).

Also, any KSPField with persistent=true will be *saved* to a node when OnSave is called (when a vessel goes on rails, for warp or for saving to .sfs or .craft) and if KSP doesn't handle your data itself you are responsible for saving persistent data in OnSave.

And you're most welcome!

Share this post


Link to post
Share on other sites

Extremely helpful, thanks for taking the time to elaborate. I'll have a look at some source that makes those calls too, it always helps to know this kind of thing in case its something I end up needing to deal with. I didn't know about the parsing of the doubles either, that may also save me a few hours in the future.

Cheers!

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