Jump to content

When the part config and the save file are different


Recommended Posts

I am experiencing some strange problems when I chnage my configurations around. In particular if I remove a MODULE from a PART in its config and add a different one, then things go a bit strange if I load a save file containing a vessel with the PART with the original MODULE. I am not sure what is going on here. I thought that the game would just create the part and at the modules as described in the save file. How can changing the .cfg file for the part influence the part in the save.

Anyone cleverer that me know the answer to this one?

Link to comment
Share on other sites

KSP has a part-loading issue: if the order of modules in the prefab differs from the order of confignodes for modules in the .craft or .sfs file's entry for that part, the [persistent] data in them won't be loaded into the prefab when it's instanced for use.

Link to comment
Share on other sites

I have to confess that I d not fully understand how the code works in relation to prefabs. I kind of get it, but it do not understand when the prefab gets created and how it is used to create the actual part.

Link to comment
Share on other sites

Well, I don't claim to have this down 100%, but here's my understanding:

On game load, the game goes through all PART{} confignodes creating Part objects (and running OnLoad for them and their modules based off the part.cfg). These are the prefabs. Then, when you place a part (in the VAB), or you load a craft file in the VAB (with its parts) or the game loads an sfs file (with vessels with parts), the game instantiates (clones) each part off the prefab, updating it via calling OnLoad fed by the peristent confignode (in .craft or .sfs) which contains all the updated persistent fields. So the prefab is cloned into a new object, and its fields (and its modules' fields) are updated to their "customized" values, to create that actual as-saved part.

Then, when the .craft is saved, or the vessel goes on rails (and eventually is saved to sfs), everything has OnSave called, which builds the persistent confignodes.

Link to comment
Share on other sites

Hmmm. OK I think I am slowly expanding my understanding on this, but there are still thinks that are not clear. My particular problem now is this:

I have a KSPAddon that in its Start method added a Module to some of the part prefabs and sets the value of persistable fields within that module. This all seems for work well. However when the prefab is cloned to create an actual Part the cloned part does have my new Module attached, but the values of the persistable fields have not been copied across to the actual part.

Any ideas on what is going on here?

Link to comment
Share on other sites

Other than the fact that serialization is screwed up *so* badly (see how much breaks when you clone a part in the VAB or do symmetry...), not offhand.

Wait, *what* fields are marked persistent? Can you paste the code block that defines them? Might help some.

Link to comment
Share on other sites

Here is some code.


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

namespace ConnectedLivingSpace
{
// This module will be added at runtime to any part that also has a ModuleDockingNode. There will be a one to one relationship between ModuleDockingHatch and ModuleDockingNode
public class ModuleDockingHatch : PartModule
{
[KSPField(isPersistant = true)]
private bool hatchOpen;

[KSPField(isPersistant = true)]
private string docNodeAttachmentNodeName;
[KSPField(isPersistant = true)]
private string docNodeTransformName;
internal ModuleDockingNode modDockNode;

public bool HatchOpen
{
get
{
return this.hatchOpen;
}

set
{
this.hatchOpen = value;

if (value)
{
this.hatchStatus = "Open";
}
else
{
this.hatchStatus = "Closed";
}
}
}

[KSPField(isPersistant = false, guiActive = true, guiName = "Hatch status")]
private string hatchStatus = "";

[KSPEvent(active = true, guiActive = true, guiName = "Open Hatch")]
private void OpenHatch()
{
bool docked = isInDockedState();

if (docked)
{
this.HatchOpen = true;
this.Events["CloseHatch"].active = true;
this.Events["OpenHatch"].active = false;
}
else
{
this.HatchOpen = false;
this.Events["CloseHatch"].active = false;
this.Events["OpenHatch"].active = false;
}

// Finally fire the VesselChange event to cause the CLSAddon to re-evaluate everything. ActiveVEssel is only available in flight, but then it should only be possible to open and close hatches in flight so we should be OK.
GameEvents.onVesselChange.Fire(FlightGlobals.ActiveVessel);
}

[KSPEvent(active = true, guiActive = true, guiName = "Close Hatch")]
private void CloseHatch()
{
bool docked = isInDockedState();

this.HatchOpen = false;

this.Events["CloseHatch"].active = false;
if (isInDockedState())
{
this.Events["OpenHatch"].active = true;
}
else
{
this.Events["OpenHatch"].active = false;
}

// Finally fire the VesselChange event to cause the CLSAddon to re-evaluate everything. ActiveVEssel is only available in flight, but then it should only be possible to open and close hatches in flight so we should be OK.
GameEvents.onVesselChange.Fire(FlightGlobals.ActiveVessel);
}

public override void OnLoad(ConfigNode node)
{
Debug.Log("OnLoad");
Debug.Log("this.docNodeAttachmentNodeName: " + this.docNodeAttachmentNodeName);
Debug.Log("this.docNodeTransformName: " + this.docNodeTransformName);
Debug.Log("node.GetValue(docNodeTransformName): " + node.GetValue("docNodeTransformName"));
Debug.Log("node.GetValue(docNodeAttachmentNodeName): " + node.GetValue("docNodeAttachmentNodeName"));

// The Loader with have set hatchOpen, but not via the Property HatchOpen, so we need to re-do it to ensure that hatchStatus gets properly set.
this.HatchOpen = this.hatchOpen;

// Set the GUI state of the open/close hatch events as appropriate
if (isInDockedState())
{
if (this.HatchOpen)
{
this.Events["CloseHatch"].active = true;
this.Events["OpenHatch"].active = false;
}
else
{
this.Events["CloseHatch"].active = false;
this.Events["OpenHatch"].active = true;
}
}
else
{
this.Events["CloseHatch"].active = false;
this.Events["OpenHatch"].active = false;
}
}

// Called every physics frame. Make sure that the menu options are valid for the state that we are in.
private void FixedUpdate()
{
if (isInDockedState())
{
if (!this.HatchOpen)
{
// We are docked, but the hatch is closed. Make sure that it is possible to open the hatch
this.Events["CloseHatch"].active = false;
this.Events["OpenHatch"].active = true;
}
}
else
{
// We are not docked - close up the hatch if it is open!
if (this.HatchOpen)
{
this.HatchOpen = false;
this.Events["CloseHatch"].active = false;
this.Events["OpenHatch"].active = false;
}
}
}

// TODO is this necassery now that we ar eusing FixedUpdate and no OnFixedUpdate?
public override void OnStart(PartModule.StartState st)
{
//Debug.Log("ModuleDockingNodeHatch::OnStart");

// As long as we have not started in the editor, ensure the module is active / enabled.
if (st != StartState.Editor)
{
//Debug.Log("ModuleDockingNodeHatch::OnStart setting enabled = true");
this.enabled = true;
}
}

private bool CheckModuleDockingNode()
{
if (null == this.modDockNode)
{
// We do not know which ModuleDockingNode we are attached to yet. Try to find one.
foreach (ModuleDockingNode dockNode in this.part.Modules.OfType<ModuleDockingNode>())
{
if (IsRelatedDockingNode(dockNode))
{
this.modDockNode = dockNode;
return true;
}
}
}
else
{
return true;
}
return false;
}

// This method allows us to check if a specified ModuleDockingNode is one that this hatch is attached to
internal bool IsRelatedDockingNode(ModuleDockingNode dockNode)
{
if (dockNode.nodeTransformName == this.docNodeTransformName)
{
this.modDockNode = dockNode;
return true;
}
if (dockNode.referenceAttachNode == this.docNodeAttachmentNodeName)
{
this.modDockNode = dockNode;
return true;
}
return false;
}

// tries to work out if the docking port is docked based on the state
private bool isInDockedState()
{
// First ensure that we know which ModuleDockingNode we are reffering to.
if (CheckModuleDockingNode())
{
if (this.modDockNode.state == "Docked (dockee)" || this.modDockNode.state == "Docked (docker)")
{
return true;
}
}
else
{
// This is bad - it means there is a hatch that we can not match to a docking node. This should not happen. We will log an error but it will likely spam the log.
Debug.LogError(" Error - Docking port hatch can not find its ModuleDockingNode docNodeTransformName:" + this.docNodeTransformName + " docNodeAttachmentNodeName " + this.docNodeAttachmentNodeName);
}

return false;
}

// Method that cna be used to set up the ModuleDockingNode that this ModuleDockingHatch reffers to.
public void AttachModuleDockingNode(ModuleDockingNode _modDocNode)
{
this.modDockNode = _modDocNode;

this.docNodeTransformName = _modDocNode.nodeTransformName;
this.docNodeAttachmentNodeName = _modDocNode.referenceAttachNode;
}
}
}

...and here is some more for the Addon that adds a ModuleDockingHatch for each ModuleDockingNode in the part.


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


namespace ConnectedLivingSpace
{
[KSPAddonFixedCLS(KSPAddon.Startup.EveryScene, false, typeof(CLSAddon))]
public class CLSAddon : MonoBehaviour
{
private static Rect windowPosition = new Rect(0,0,320,360);
private static GUIStyle windowStyle = null;

private Vector2 scrollViewer = Vector2.zero;

private CLSVessel vessel = null;
private int selectedSpace = -1;

private IButton toolbarButton = null; // Toolbar button
private bool visable = false;

private int editorPartCount = 0; // This is horrible. Because there does not seem to be an obvious callback to sink when parts are added and removed in the editor, on each fixed update we will could the parts and if it has changed then rebuild the CLSVessel. Yuk!

private int sanityCheckCounter = 0;
private int sanityCheckFrequency = 100; // Change this to make the sanity checks more or less frequent.

private string spaceNameEditField;

public CLSVessel Vessel
{
get
{
return this.vessel;
}
}

public static CLSAddon Instance
{
get;
private set;
}

public CLSAddon()
{
if (Instance == null)
{
Instance = this;
}
}

public void Awake()
{
//Debug.Log("CLSAddon:Awake");

this.toolbarButton = ToolbarManager.Instance.add("ConnectedLivingSpace", "buttonCLS");
this.toolbarButton.TexturePath = "ConnectedLivingSpace/assets/cls_icon_off";
this.toolbarButton.ToolTip = "Connected Living Space";
this.toolbarButton.OnClick += (e) => { OnToolbarButton_Click(); };
this.toolbarButton.Visibility = new GameScenesVisibility(GameScenes.EDITOR, GameScenes.SPH, GameScenes.FLIGHT);

this.selectedSpace = -1;
}

public void Start()
{
// Debug.Log("CLSAddon:Start");

windowStyle = new GUIStyle(HighLogic.Skin.window);

try
{
RenderingManager.RemoveFromPostDrawQueue(0, OnDraw);
}
catch (Exception ex)
{
Debug.LogException(ex);
}

if (HighLogic.LoadedSceneIsEditor || HighLogic.LoadedSceneIsFlight)
{
RenderingManager.AddToPostDrawQueue(0, OnDraw);
GameEvents.onJointBreak.Add(OnJointBreak);
GameEvents.onPartAttach.Add(OnPartAttach);
GameEvents.onPartCouple.Add(OnPartCouple);
GameEvents.onPartDie.Add(OnPartDie);
GameEvents.onPartExplode.Add(OnPartExplode);
GameEvents.onPartRemove.Add(OnPartRemove);
GameEvents.onPartUndock.Add(OnPartUndock);
GameEvents.onStageSeparation.Add(OnStageSeparation);
GameEvents.onUndock.Add(OnUndock);
GameEvents.onVesselCreate.Add(OnVesselCreate);
GameEvents.onVesselDestroy.Add(OnVesselDestroy);
GameEvents.onVesselWasModified.Add(OnVesselWasModified);
GameEvents.onVesselChange.Add(OnVesselChange);
GameEvents.onVesselLoaded.Add(OnVesselLoaded);
GameEvents.onVesselTerminated.Add(OnVesselTerminated);
GameEvents.onFlightReady.Add(OnFlightReady);

}

// Add the CLSModule to all parts that can house crew (and do not already have it).
AddModuleToParts();

// Add the ModuleDockingNodeHatch to all the Docking Nodes
AddHatchModuleToParts();
}

private void OnToolbarButton_Click()
{
//Debug.Log("OnToolbarButton_Click");

// If the window is currently visible, set the selected space back to -1 so the highlighting is cleared.
if (this.visable)
{
if (null != this.vessel)
{
vessel.Highlight (false);
}
this.selectedSpace = -1;
this.toolbarButton.TexturePath = "ConnectedLivingSpace/assets/cls_icon_off";
}
else
{
this.toolbarButton.TexturePath = "ConnectedLivingSpace/assets/cls_icon_on";
}

this.visable = !this.visable;
}

private void OnFlightReady()
{
//Debug.Log("CLSAddon::OnFlightReady");

// Now scan the vessel
//Debug.Log("Calling RebuildCLSVessel from onFlightReady");
this.RebuildCLSVessel();
}

private void OnVesselLoaded(Vessel data)
{
//Debug.Log("CLSAddon::OnVesselLoaded");

//Debug.Log("Calling RebuildCLSVessel from OnVesselLoaded");
RebuildCLSVessel(data);
}
private void OnVesselTerminated(ProtoVessel data)
{
//Debug.Log("CLSAddon::OnVesselTerminated");
}
private void OnJointBreak(EventReport eventReport)
{
//Debug.Log("CLSAddon::OnJointBreak");
}
private void OnPartAttach(GameEvents.HostTargetAction<Part, Part> data)
{
//Debug.Log("CLSAddon::OnPartAttach");
}
private void OnPartCouple(GameEvents.FromToAction <Part, Part> data)
{
//Debug.Log("CLSAddon::OnPartCouple");
}
private void OnPartDie(Part data)
{
//Debug.Log("CLSAddon::OnPartDie");
}
private void OnPartExplode(GameEvents.ExplosionReaction data)
{
//Debug.Log("CLSAddon::OnPartExplode");
}
private void OnPartRemove(GameEvents.HostTargetAction<Part, Part> data)
{
//Debug.Log("CLSAddon::OnPartRemove");
}
private void OnPartUndock(Part data)
{
//Debug.Log("CLSAddon::OnPartUndock");
}
private void OnStageSeparation(EventReport eventReport)
{
//Debug.Log("CLSAddon::OnStageSeparation");
}
private void OnUndock(EventReport eventReport)
{
//Debug.Log("CLSAddon::OnUndock");
}
private void OnVesselDestroy(Vessel data)
{
//Debug.Log("CLSAddon::OnVesselDestroy");
}
private void OnVesselCreate(Vessel data)
{
//Debug.Log("CLSAddon::OnVesselCreate");
}
private void OnVesselWasModified(Vessel data)
{
//Debug.Log("CLSAddon::OnVesselWasModified");

//Debug.Log("Calling RebuildCLSVessel from OnVesselWasModified");

RebuildCLSVessel(data);
}

// This event is fired when the vessel is changed. If this happens we need to throw away all of our thoiughts about the previous vessel, and analyse the new one.
private void OnVesselChange(Vessel data)
{
//Debug.Log("CLSAddon::OnVesselChange");

//Debug.Log("Calling RebuildCLSVessel from OnVesselChange");
RebuildCLSVessel(data);
}

private void OnDraw()
{
if (this.visable)
{
windowPosition = GUILayout.Window(947695, windowPosition, OnWindow, "Connected Living Space", windowStyle,GUILayout.MinHeight(20));
}
}


private void RebuildCLSVessel()
{
if (HighLogic.LoadedSceneIsFlight)
{
RebuildCLSVessel(FlightGlobals.ActiveVessel);
}
else if (HighLogic.LoadedSceneIsEditor)
{
RebuildCLSVessel(EditorLogic.startPod);
}
}

private void RebuildCLSVessel(Vessel newVessel)
{
RebuildCLSVessel(newVessel.rootPart);
}

private void RebuildCLSVessel(Part newRootPart)
{
//Debug.Log("RebuildCLSVessel");
// Before we rebuild the vessel, we need to take some steps to tidy up the highlighting and our idea of which space is the selected space. We will make a list of all the parts that are currently in the selected space. We will also unhighlight parts that are highlighted. Once the rebuild is complete we will work out which space will be the selected space based on the first part in our list that we find in oneof the new spaces. We can then highlight that new space.

List<uint> listSelectedParts = new List<uint>();

if (-1 != selectedSpace)
{
foreach (CLSPart p in vessel.Spaces[selectedSpace].Parts)
{
Part part = (Part)p;
listSelectedParts.Add(part.flightID);
//Debug.Log("Part : "+ part.flightID + " currently in use." ) ;
}

vessel.Spaces[selectedSpace].Highlight(false);
}

//Debug.Log("Old selected space had "+listSelectedParts.Count + " parts in it.");

// Tidy up the old vessel information
if (null != this.vessel)
{
vessel.Clear();
}
this.vessel = null;

// Build new vessel information
this.vessel = new CLSVessel();
this.vessel.Populate(newRootPart);

// Now work out which space should be highlighted.
this.selectedSpace = -1;
foreach (CLSPart clsPart in this.vessel.Parts)
{
Part p = clsPart;

//Debug.Log("New vessel contains part : " + p.flightID);

if (listSelectedParts.Contains(p.flightID))
{
//Debug.Log("Part " + p.partInfo.title + " was in the old selected space and is in the CLSVessel");
if (clsPart.Space != null)
{
// We have found the new space for a part that was in the old selected space.
this.selectedSpace = this.vessel.Spaces.IndexOf(clsPart.Space);
//Debug.Log("... it is also part of a space. We will use that space to be our new selected space. index:" + this.selectedSpace);
break;
}
else
{
//Debug.Log("it is no longer part of a space :(");
}
}
}

if (this.selectedSpace != -1)
{
this.vessel.Spaces[this.selectedSpace].Highlight(true);
}
else
{
//Debug.Log("No space is selected after the rebuild.");
}

// Sanity check the selected space. If the CLSvessel has been rebuilt and there are no Spaces, or it references an out of range space then set it to -1

if (vessel.Spaces.Count == 0 || vessel.Spaces.Count <= this.selectedSpace)
{
this.selectedSpace = -1;
}
}

private void OnWindow(int windowID)
{
try
{
// Build a string descibing the contents of each of the spaces.
if (null != this.vessel)
{
GUILayout.BeginVertical();

String[] spaceNames = new String[vessel.Spaces.Count];
int counter = 0;
int newSelectedSpace = -1;

String partsList = "";
foreach (CLSSpace space in vessel.Spaces)
{
if (space.Name == "")
{
spaceNames[counter] = "Living Space " + (counter + 1).ToString();
}
else
{
spaceNames[counter] = space.Name;
}
counter++;
}

if (vessel.Spaces.Count > 0)
{
newSelectedSpace = GUILayout.SelectionGrid(this.selectedSpace, spaceNames, counter);
}

// If one of the spaces has been selected then display a list of parts that make it up and sort out the highlighting
if (-1 != newSelectedSpace)
{
// Only fiddle witht he highlighting is the selected space has actually changed
if (newSelectedSpace != this.selectedSpace)
{
// First unhighlight the space that was selected.
if (-1 != this.selectedSpace && this.selectedSpace < this.vessel.Spaces.Count)
{
vessel.Spaces[this.selectedSpace].Highlight(false);
}

// Update the space that has been selected.
this.selectedSpace = newSelectedSpace;

// Update the text in the Space edit box
this.spaceNameEditField = vessel.Spaces[this.selectedSpace].Name;

// Highlight the new space
vessel.Spaces[this.selectedSpace].Highlight(true);
}

// Loop through all the parts in the newly selected space and create a list of all the spaces in it.
foreach (CLSPart p in vessel.Spaces[this.selectedSpace].Parts)
{
Part part = (Part)p;
partsList += part.partInfo.title + "\n";
}

// Display the text box that allows the space name to be changed
GUILayout.BeginHorizontal();
GUILayout.Label("Space Name:");
this.spaceNameEditField = GUILayout.TextField(this.spaceNameEditField);
if (GUILayout.Button("Update"))
{
vessel.Spaces[this.selectedSpace].Name = this.spaceNameEditField;
}
GUILayout.EndHorizontal();

this.scrollViewer = GUILayout.BeginScrollView(this.scrollViewer,GUILayout.ExpandHeight(true),GUILayout.ExpandWidth(true));
GUILayout.BeginVertical();

// Display the crew capacity of the space.
GUILayout.Label("Crew Capacity: " + vessel.Spaces[this.selectedSpace].MaxCrew);

// And list the crew names
String crewList = "Crew Info:\n";

foreach(CLSKerbal crewMember in vessel.Spaces[this.selectedSpace].Crew)
{
crewList += ((ProtoCrewMember)crewMember).name +"\n";
}
GUILayout.Label(crewList);

// Display the list of component parts.
GUILayout.Label(partsList);

GUILayout.EndVertical();
GUILayout.EndScrollView();

}
GUILayout.EndVertical();
}
else
{
Debug.LogError("this.vessel was null");
}


GUI.DragWindow();
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}

public void Update()
{
// Debug.Log("CLSAddon:Update");
}

public void FixedUpdate()
{
try
{
// Debug.Log("CLSAddon:FixedUpdate");

// If we are in the editor, and there is a ship in the editor, then compare the number of parts to last time we did this. If it has changed then rebuild the CLSVessel
if (HighLogic.LoadedSceneIsEditor)
{
int currentPartCount = 0;
if (null == EditorLogic.startPod)
{
currentPartCount = 0; // I know that this is already 0, but just to make the point - if there is no startPod in the editor, then there are no parts in the vessel.
}
else
{
currentPartCount = EditorLogic.SortedShipList.Count;
}

if (currentPartCount != this.editorPartCount)
{
Debug.Log("Calling RebuildCLSVessel as the part count has changed in the editor");

this.RebuildCLSVessel();
this.editorPartCount = currentPartCount;
}
}
else if (HighLogic.LoadedSceneIsFlight)
{
// In flight, run the sanity checker.
if (FlightGlobals.ready)
{
// Do not run the sanity checker if the CLSVessel (and hence all the CLS parts) has not yet been constructed.
if (null != this.vessel)
{
// Only run the sanity check every now and again!
this.sanityCheckCounter++;
this.sanityCheckCounter = this.sanityCheckCounter % this.sanityCheckFrequency;

// Debug.Log("sanityCheckCounter: " + sanityCheckCounter);

if (1 == this.sanityCheckCounter) // but running the checker when the counter is one, we know that we can force the check on the next physics frame by setting it to 0.
{
this.SanityCheck();
}
}
}
}
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}

public void OnDestroy()
{
//Debug.Log("CLSAddon::OnDestroy");
GameEvents.onVesselWasModified.Remove(OnVesselWasModified);
GameEvents.onVesselChange.Remove(OnVesselChange);
GameEvents.onJointBreak.Remove(OnJointBreak);
GameEvents.onPartAttach.Remove(OnPartAttach);
GameEvents.onPartCouple.Remove(OnPartCouple);
GameEvents.onPartDie.Remove(OnPartDie);
GameEvents.onPartExplode.Remove(OnPartExplode);
GameEvents.onPartRemove.Remove(OnPartRemove);
GameEvents.onPartUndock.Remove(OnPartUndock);
GameEvents.onStageSeparation.Remove(OnStageSeparation);
GameEvents.onUndock.Remove(OnUndock);
GameEvents.onVesselCreate.Remove(OnVesselCreate);
GameEvents.onVesselDestroy.Remove(OnVesselDestroy);
GameEvents.onVesselWasModified.Remove(OnVesselWasModified);
GameEvents.onVesselChange.Remove(OnVesselChange);
GameEvents.onVesselTerminated.Remove(OnVesselTerminated);
GameEvents.onVesselLoaded.Remove(OnVesselLoaded);
GameEvents.onFlightReady.Remove(OnFlightReady);

// Remove the toolbar button

this.toolbarButton.Destroy();
}

// Method to ensure that all parts which have a crewcapacity >0 have a CLSModule attached to it.
private void AddModuleToParts()
{
IEnumerable<AvailablePart> parts = PartLoader.LoadedPartsList.Where(p => p.partPrefab != null && p.partPrefab.CrewCapacity > 0);
foreach (AvailablePart part in parts)
{
try
{
if (part.name.Equals("kerbalEVA"))
{
// Debug.Log("No CLS required for KerbalEVA!");
}
else
{
Part prefabPart = part.partPrefab;

//Debug.Log("Adding ConnectedLivingSpace Support to " + part.name + "/" + prefabPart.partInfo.title);

if (!prefabPart.Modules.Contains("ModuleConnectedLivingSpace"))
{
//Debug.Log("The ModuleConnectedLivingSpace is missing!");

ConfigNode node = new ConfigNode("MODULE");
node.AddValue("name", "ModuleConnectedLivingSpace");
{
// This block is required as calling AddModule and passing in the node throws an exception if Awake has not been called. The method Awaken uses reflection to call then private method Awake. See http://forum.kerbalspaceprogram.com/threads/27851 for more information.
PartModule pm = prefabPart.AddModule("ModuleConnectedLivingSpace");
if (Awaken(pm))
{
pm.Load(node);
}
}
}
else
{
// Debug.Log("The ModuleConnectedLivingSpace is already there.");
}
}
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
}

// Method to add Docking Hatches to all pars that have Dockong Nodes
private void AddHatchModuleToParts()
{
IEnumerable<AvailablePart> parts = PartLoader.LoadedPartsList.Where(p => p.partPrefab != null);
foreach (AvailablePart part in parts)
{
try
{
Part prefabPart = part.partPrefab;

// If the prefab part does not have any modules set up then move to the next part
if (null == prefabPart.Modules)
{
continue;
}

List<ModuleDockingNode> listDockNodes = new List<ModuleDockingNode>();
List<ModuleDockingHatch> listDockHatches = new List<ModuleDockingHatch>();

// Build a temporary list of docking node to consider. This is necassery can we can not add hatch modules to the modules list while we are enumerating the very same list!
foreach (ModuleDockingNode dockNode in prefabPart.Modules.OfType<ModuleDockingNode>())
{
listDockNodes.Add(dockNode);
}

foreach (ModuleDockingHatch dockHatch in prefabPart.Modules.OfType<ModuleDockingHatch>())
{
listDockHatches.Add(dockHatch);
}

foreach (ModuleDockingNode dockNode in listDockNodes)
{
// Does this docking node have a corresponding hatch?
ModuleDockingHatch hatch = null;
foreach (ModuleDockingHatch h in listDockHatches)
{
if (h.IsRelatedDockingNode(dockNode))
{
hatch = h;
break;
}
}

if (null == hatch)
{
// There is no corresponding hatch - add one.
ConfigNode node = new ConfigNode("MODULE");
node.AddValue("name", "ModuleDockingHatch");

if (dockNode.referenceAttachNode != string.Empty)
{
Debug.Log("Adding ModuleDockingHatch to part " + prefabPart.partInfo.title + " and the docking node that uses attachNode " + dockNode.referenceAttachNode);
node.AddValue("docNodeAttachmentNodeName", dockNode.referenceAttachNode);
}
else
{
if (dockNode.nodeTransformName != string.Empty)
{
Debug.Log("Adding ModuleDockingHatch to part " + prefabPart.partInfo.title + " and the docking node that uses transform " + dockNode.nodeTransformName);
node.AddValue("docNodeTransformName", dockNode.nodeTransformName);
}
}

{
// This block is required as calling AddModule and passing in the node throws an exception if Awake has not been called. The method Awaken uses reflection to call then private method Awake. See http://forum.kerbalspaceprogram.com/threads/27851 for more information.
PartModule pm = prefabPart.AddModule("ModuleDockingHatch");
if (Awaken(pm))
{
Debug.Log("Loading the ModuleDockingHatch config");
pm.Load(node);
}
else
{
Debug.Log("Failed to call Awaken so the config has not been loaded.");
}
}
}
}
}
catch (Exception ex)
{
Debug.LogException(ex);
}
}
}



//This method uses reflection to call the Awake private method in PartModule. It turns out that Part.AddModule fails if Awake has not been called (which sometimes it has not). See http://forum.kerbalspaceprogram.com/threads/27851 for more info on this.
public static bool Awaken(PartModule module)
{
// thanks to Mu and Kine for help with this bit of Dark Magic.
// KINEMORTOBESTMORTOLOLOLOL
if (module == null)
return false;
object[] paramList = new object[] { };
MethodInfo awakeMethod = typeof(PartModule).GetMethod("Awake", BindingFlags.Instance | BindingFlags.NonPublic);

if (awakeMethod == null)
return false;

awakeMethod.Invoke(module, paramList);
return true;
}

// Utility method that is run every now an again and just checks that everything is in sync and makes sense. The actualt funtionailty in a method on the module class.
private void SanityCheck()
{
foreach(Part p in FlightGlobals.ActiveVessel.Parts)
{
foreach (ModuleConnectedLivingSpace clsmod in p.Modules.OfType<ModuleConnectedLivingSpace>())
{
//clsmod.SanityCheck();
}
}
}
}
}

Link to comment
Share on other sites

Hmmm, I have never really used IRC, and last time I tried I could not get it to work, but perhaps I will give it a go.

I think I have concluded that my premise is just wrong. Setting values in the module of a prefab will simply not work. While the prefab gets cloned when a part is created, its modules will not get any special creation - just the constructor, and a call to OnInitialize (which would not help me).

I think I need to use FixedUpdate to somehow check for any parts with ModuleDockingNodes that do not have a corresponding ModuleDockingHatch. The challenge is to do this without trashing the processor :( ......

(Thanks for taking the time to look at my code BTW - it is never pleasant having to wade through other people's stuff)

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...