Jump to content

Unable to read part.cfg data when part is added to vessel


Recommended Posts

Hey everyone,

This is driving me absolutely crazy! I was going back and forth on this in the helper thread, but decided I was spamming that too much so i'm starting a thread on it.

This is the relevant part of my part's config file:


MODULE
{
name = FlightDataRecorder
testValue = 42.1
BODY
{
name = kerbin
amount = 1.0
}
SITUATION
{
name = landed
value = 0.0
}
SITUATION
{
name = flying
value = 1.0
}
SITUATION
{
name = prelaunch
value = 0.0
}
}

Basically through my debugs I can see that the CFG data is visible during the OnLoad() method at game start when the game is loading up and parsing all the parts. However when I go to make a vessel and add my part to the vessel, the OnLoad() being called in the FLIGHT scene does not contain the data from the part.cfg.

I've been looking at other author's code, and I as best as I can tell i'm doing the same thing they are (I looked at @HoneyFox EngineIgnitor mod and @NathanKell ModuleFuels mod), but it doesn't work for me. Every single time my OnLoad() is called when putting my part on a vessel, the data is gone.

Here is all my relevant code.

IConfigNode


[Serializable]
public class FlightDataRecorderBody : IConfigNode
{
[SerializeField]
public string name;
[SerializeField]
public float amount;

public float currentAmount;

public FlightDataRecorderBody()
{
}

public void Load(ConfigNode node)
{
name = node.GetValue("name");

if (node.HasValue("amount"))
{
amount = Mathf.Max(0.0f, float.Parse(node.GetValue("amount")));
}
}

public void Save(ConfigNode node)
{
node.AddValue("name", name);
node.AddValue("amount", Mathf.Max(0.0f, amount));
}

public override string ToString()
{
return name + "(" + amount.ToString("F3") + ")";
}

public static FlightDataRecorderBody FromString(string str)
{
FlightDataRecorderBody bodyConfig = new FlightDataRecorderBody();
int indexL = str.LastIndexOf('('); int indexR = str.LastIndexOf(')');
bodyConfig.name = str.Substring(0, indexL);
bodyConfig.amount = float.Parse(str.Substring(indexL + 1, indexR - indexL - 1));
return bodyConfig;
}
}

PartModule


public class FlightDataRecorder : PartModule
{
private bool isRecordingFlightData = false;
public List<FlightDataRecorderBody> bodyConfigs;

public override void OnAwake()
{
base.OnAwake();

if (bodyConfigs == null)
bodyConfigs = new List<FlightDataRecorderBody>();
}
public override void OnLoad(ConfigNode node)
{
base.OnLoad(node);
print("FlightDataRecorder: OnLoad");
print("FlightDataRecorder: " + node.ToString());
if (bodyConfigs == null)
bodyConfigs = new List<FlightDataRecorderBody>();
foreach (ConfigNode bodyConfig in node.GetNodes("BODY"))
{
print("LOADING: ");
print(bodyConfig.ToString());
FlightDataRecorderBody flightBody = new FlightDataRecorderBody();
flightBody.Load(bodyConfig);
bodyConfigs.Add(flightBody);
}
}

public override void OnStart(StartState state)
{
print("FlightDataRecorder: onStart()");
print("FlightDataRecorder: State = " + state);
}
public override void OnUpdate()
{
print("FlightDataRecorder: onUpdate");
foreach (FlightDataRecorderBody body in bodyConfigs)
{
print(body.ToString());
}
}

public void Update()
{
//print("FlightDataRecorder: Update");
}
public override void OnSave(ConfigNode node)
{
print("FlightDataRecorder: onSave()");
foreach (FlightDataRecorderBody bodyConfig in bodyConfigs)
{
print("SAVING:");
print(bodyConfig.ToString());
bodyConfig.Save(node.AddNode("BODY"));
}
base.OnSave(node);
}

Help me internets, you're my only hope! (Seriously i'm going absolutely batty here!)

Link to comment
Share on other sites

This would be because the only time the original part.cfg is passed to OnLoad is when the part prefab is created.

Every other time a part is instantiated (and OnLoad for its modules is called), it's reading data from persistence to modify the part from the prefab's settings (i.e. loading a craft, or loading a vessel in flight from sfs--they are the same format because they're doing the same thing, storing the only bits of data that change from the part.cfg).

If you really, really, really need to see that data again in OnLoad, flag it as persistent=true. That will make OnSave save it to .craft/.sfs, which means it will be there for OnLoad.

To go back a bit: when you load a craft (in VAB/SPH, in flight, whatever) the part objects (and their modules) aren't created de novo. Instead, only one "prefab" of each object is created at KSP start: one prefab part per PART, with prefab modules per MODULE. Then, when the game actually wants to put a part "into action" (loading a vessel into flight [off rails], loading a vessel in the editor) it's cloned from the prefab, and any "persistent" settings are loaded to override the prefab setting. Conversely, whenever a part is saved to ConfigNode (vessel goes on rails, game is saved, craft is saved in editor, etc) then only persistent settings are written to ConfigNode; any members of your module (or any other module) that are not persistent=true KSPFields will not get saved, and will therefore return to their prefab values the next time the object is cloned.

What makes this harder, of course, is that KSP isn't very good about cloning data from the prefab into the instantiated partmodule. So sometimes you need to use workarounds.

Link to comment
Share on other sites

But how can I make a complex datatype (In my case public List<FlightDataRecorderBody> bodyConfigs) persist? It isn't a simple datatype that I can just make a KSPField.

I can get it to work with simple datatypes like strings and floats, but not with whole confignodes.

What further confuses me is that this case is almost a direct plagiarism from HoneyFox's code which obviously works. And as best I can tell i'm pretty much doing the same thing you are doing in ModularFuels, though less complicated.

Link to comment
Share on other sites

Ok well looks like i've managed to hack my way around it, but the odd part is I don't see how what I did even works.

I'll post details after dinner. But basically the List I was storing the IConfigNode class instances in, was NOT persisting between the intial Load and the instance. But for some reason, if I store those as packed strings in another list, those DO persist, even though i'm not making them a KSPField. WTF.

Link to comment
Share on other sites

Ok that makes sense in an odd way, but what confuses me is that I didn't tell them to be serialized. I didn't make the strings into a KSPField, so something must happen automatically to properties behind the scenes that are "whitelisted". Very confusing!

Link to comment
Share on other sites

So for future posterity in case anyone else runs into the same problem, i'm not going to be one of those jerks who posts a problem then says i've solved it without posting the solution :)

Below is the full final code that makes this work. Basically though, the solution was to add a new property to my PartModule class that is a List of strings. Then in OnLoad() when I load in my ConfigNode, I also pack the data into strings and add them to that list. Then later in OnStart() I recreated my ConfigNodes from those strings, since the strings seem to survive through where the ConfigNodes wouldnt.

The IConfigNode class doesn't change:


[Serializable]
public class FlightDataRecorderBody : IConfigNode
{
[SerializeField]
public string name;
[SerializeField]
public float amount;

public float currentAmount;

public FlightDataRecorderBody()
{
}

public void Load(ConfigNode node)
{
name = node.GetValue("name");

if (node.HasValue("amount"))
{
amount = Mathf.Max(0.0f, float.Parse(node.GetValue("amount")));
}
}

public void Save(ConfigNode node)
{
node.AddValue("name", name);
node.AddValue("amount", Mathf.Max(0.0f, amount));
}

public override string ToString()
{
return name + "(" + amount.ToString("F3") + ")";
}

public static FlightDataRecorderBody FromString(string str)
{
FlightDataRecorderBody bodyConfig = new FlightDataRecorderBody();
int indexL = str.LastIndexOf('('); int indexR = str.LastIndexOf(')');
bodyConfig.name = str.Substring(0, indexL);
bodyConfig.amount = float.Parse(str.Substring(indexL + 1, indexR - indexL - 1));
return bodyConfig;
}
}

And here is the updated PartModule class (note that I did restructure slightly from last night, moving some init code into its own method to keep it DRY):


public class FlightDataRecorder : PartModule
{
private bool isRecordingFlightData = false;

public List<string> bodyConfigsStr;
public List<FlightDataRecorderBody> bodyConfigs;

public void initStorage()
{
if (bodyConfigs == null)
bodyConfigs = new List<FlightDataRecorderBody>();
if (bodyConfigsStr == null)
bodyConfigsStr = new List<string>();
}
public override void OnAwake()
{
base.OnAwake();
initStorage();
}
public override void OnLoad(ConfigNode node)
{
base.OnLoad(node);
print("FlightDataRecorder: OnLoad");
print("FlightDataRecorder: " + node.ToString());
initStorage();
foreach (ConfigNode bodyConfig in node.GetNodes("BODY"))
{
print("LOADING: ");
print(bodyConfig.ToString());
FlightDataRecorderBody flightBody = new FlightDataRecorderBody();
flightBody.Load(bodyConfig);
bodyConfigs.Add(flightBody);
bodyConfigsStr.Add(flightBody.ToString());
}
}

public override void OnStart(StartState state)
{
print("FlightDataRecorder: onStart()");
print("FlightDataRecorder: State = " + state);
bodyConfigs.Clear();
situationConfigs.Clear();
foreach (string bodyStr in bodyConfigsStr)
{
print("BODY_STR:" + bodyStr);
bodyConfigs.Add(FlightDataRecorderBody.FromString(bodyStr));
}
}
public override void OnUpdate()
{
}

public override void OnSave(ConfigNode node)
{
print("FlightDataRecorder: onSave()");
foreach (FlightDataRecorderBody bodyConfig in bodyConfigs)
{
print("SAVING:");
print(bodyConfig.ToString());
bodyConfig.Save(node.AddNode("BODY"));
}
base.OnSave(node);
}

Link to comment
Share on other sites

I still can't get a custom ConfigNode to persist and I don't understand why. I found the original Dev post on these features, and i've basically copied what they did, but it just refuses to work with anything but basic values, even if I try to persist a class that implements IConfigNode.

Link to comment
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.

×
×
  • Create New...