Skip to content
This repository has been archived by the owner on Oct 26, 2020. It is now read-only.

Commit

Permalink
Update ShipSaveSplicer to enable crew import again. Add a bunch of ad…
Browse files Browse the repository at this point in the history
…ditional logging to SSS.
  • Loading branch information
magico13 committed Mar 28, 2018
1 parent 653b18e commit 33a049a
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 49 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,4 @@ _Pvt_Extensions/
ModelManifest.xml
/.vs/Modlets/v15/Server/sqlite3
/.vs/slnx.sqlite
/.vs/VSWorkspaceState.json
4 changes: 2 additions & 2 deletions ShipSaveSplicer/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.1.4.0")]
[assembly: AssemblyFileVersion("1.1.4.0")]
[assembly: AssemblyVersion("1.2.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")]
127 changes: 80 additions & 47 deletions ShipSaveSplicer/ShipSaveSplicer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,33 @@ namespace ShipSaveSplicer
[KSPAddon(KSPAddon.Startup.TrackingStation, false)]
public class ShipSaveSplicer : MonoBehaviour
{
public static ApplicationLauncherButton theButton;
public static bool includeCrew = false;
private static bool EventAdded = false;
private static ApplicationLauncherButton _theButton;
private static bool _includeCrew = false;
private static bool _eventAdded = false;
public void Start()
{
//add button to the Stock toolbar
if (!EventAdded)
if (!_eventAdded)
{
GameEvents.onGUIApplicationLauncherReady.Add(AddButton);
EventAdded = true;
_eventAdded = true;
}

//AddButton();
}

public void OnDestroy()
{
if (theButton != null)
if (_theButton != null)
{
ApplicationLauncher.Instance.RemoveModApplication(theButton);
theButton = null;
ApplicationLauncher.Instance.RemoveModApplication(_theButton);
_theButton = null;
}
}

public void AddButton()
{
if (ApplicationLauncher.Ready && theButton == null)
if (ApplicationLauncher.Ready && _theButton == null && HighLogic.LoadedScene == GameScenes.TRACKSTATION)
{
theButton = ApplicationLauncher.Instance.AddModApplication(
_theButton = ApplicationLauncher.Instance.AddModApplication(
OnClick,
Dummy, Dummy, Dummy, Dummy, Dummy, ApplicationLauncher.AppScenes.TRACKSTATION, GameDatabase.Instance.GetTexture("ShipSaveSplicer/icon", false));
}
Expand All @@ -50,22 +48,22 @@ private void Dummy() { }
public void OnClick()
{
//figure out if mod+clicked
//includeCrew = GameSettings.MODIFIER_KEY.GetKey(); //TODO: Reenable when fixed
includeCrew = false;
_includeCrew = GameSettings.MODIFIER_KEY.GetKey();
//includeCrew = false;
bool ctrlHeld = Input.GetKey(KeyCode.LeftControl);

if (Input.GetKey(KeyCode.LeftShift))
{
OpenConvertWindow(); //convert ships to craft files
theButton.SetFalse();
_theButton.SetFalse();
return;
}

//get the selected craft
SpaceTracking trackingStation = (SpaceTracking)FindObjectOfType(typeof(SpaceTracking));
Vessel selectedVessel = trackingStation.SelectedVessel;

if (ctrlHeld || includeCrew) //ctrl or modifier held
if (ctrlHeld || _includeCrew) //ctrl or modifier held
{
OpenImportWindow();
}
Expand All @@ -74,14 +72,16 @@ public void OnClick()
ExportSelectedCraft(selectedVessel);
}

theButton.SetFalse(false);
_theButton.SetFalse(false);
}

public void ExportSelectedCraft(Vessel vessel)
{
CreateFolder();

string filename = KSPUtil.ApplicationRootPath + "/Ships/export/" + HighLogic.SaveFolder + "_" + vessel.vesselName;
Log($"Exporting vessel: {vessel.vesselName}\nExporting to file: {filename}");

ConfigNode nodeToSave = new ConfigNode();

//save vessel
Expand Down Expand Up @@ -116,7 +116,7 @@ public void OpenConvertWindow()
for (int i = 0; i < count; i++)
{
int select = i;
options[i] = new DialogGUIButton(files[i].Split('/').Last(), () => { ConvertVessel(files[select]); });
options[i] = new DialogGUIButton(files[i].Split('/').Last(), () => { ConvertToCraft(files[select]); });
}
options[count] = new DialogGUIButton("Close", Dummy);
string msg = "Select a vessel to convert to a .craft file.";
Expand All @@ -125,21 +125,17 @@ public void OpenConvertWindow()
PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), a, false, HighLogic.UISkin);
}

public void ConvertVessel(string name)
public void ConvertToCraft(string name)
{
if (System.IO.File.Exists(name))
{
Log($"Converting ship to craft file: {name}");
ConfigNode storedNode = ConfigNode.Load(name);

ConfigNode vesselNode = storedNode.GetNode("VESSEL");

List<string> invalidParts = InvalidParts(vesselNode);
if (invalidParts.Count > 0) //contains invalid parts and can't be loaded
if (WarnOfInvalidParts(vesselNode, false))
{
StringBuilder msg = new StringBuilder("The selected vessel cannot be converted because it contains the following invalid parts (perhaps you removed a mod?):\n");
foreach (string invalid in invalidParts)
msg.Append(" ").AppendLine(invalid);
PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), "missingPartsPopup", "Missing Parts", msg.ToString(), "Ok", false, HighLogic.UISkin);
return;
}
//clear out all crew on vessel
Expand All @@ -165,14 +161,13 @@ public void VesselToCraftFile(ConfigNode VesselNode)
ProtoVessel VesselToSave = HighLogic.CurrentGame.AddVessel(VesselNode);
if (VesselToSave.vesselRef == null)
{
Debug.LogError("Vessel reference is null!");
Log("Vessel reference is null!");
return;
}

try
{
string ShipName = VesselToSave.vesselName;
// Debug.LogWarning("Saving: " + ShipName);

//Vessel FromFlight = FlightGlobals.Vessels.Find(v => v.id == VesselToSave.vesselID);
try
Expand All @@ -182,7 +177,7 @@ public void VesselToCraftFile(ConfigNode VesselNode)
catch (Exception ex)
{
Debug.LogException(ex);
Debug.Log("Attempting to continue.");
Log("Attempting to continue.");
}

ShipConstruct ConstructToSave = new ShipConstruct(ShipName, "", VesselToSave.vesselRef.Parts[0]);
Expand Down Expand Up @@ -247,8 +242,7 @@ public void OpenImportWindow()
options[i] = new DialogGUIButton(files[i].Split('/').Last(), () => { ImportVessel(files[select]); });
}
options[count] = new DialogGUIButton("Close", Dummy);
string msg = "Select a vessel to import. Will " + (includeCrew ? "" : "not ") + "import crew members.";// +(includeCrew ? "": " (modifier+click the SSS button to include crew)");
//TODO: Reenable when fixed
string msg = string.Format("Select a vessel to import. Will {0}import crew.{1}", (_includeCrew ? string.Empty : "not "), (_includeCrew ? string.Empty : "\n(modifier+click the SSS button to include crew)"));

MultiOptionDialog a = new MultiOptionDialog("importPopup", msg, "Import Vessel", null, options);
PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), a, false, HighLogic.UISkin);
Expand All @@ -258,25 +252,21 @@ public void ImportVessel(string name)
{
if (System.IO.File.Exists(name))
{
Log($"Importing vessel: {name}");
ConfigNode storedNode = ConfigNode.Load(name);

ConfigNode vesselNode = storedNode.GetNode("VESSEL");

vesselNode.SetValue("pid", Guid.NewGuid().ToString());

List<string> invalidParts = InvalidParts(vesselNode);
if (invalidParts.Count > 0) //contains invalid parts and can't be loaded
if (WarnOfInvalidParts(vesselNode, true))
{
string msg = "The selected vessel cannot be imported because it contains the following invalid parts (perhaps you removed a mod?):\n";
foreach (string invalid in invalidParts)
msg += " " + invalid + "\n";
PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), "missingPartsPopup", "Missing Parts", msg, "Ok", false, HighLogic.UISkin);
return;
}

List<ProtoCrewMember> crewAdded = new List<ProtoCrewMember>();


if (!includeCrew)
if (!_includeCrew)
{
//clear out all crew on vessel
StripCrew(vesselNode);
Expand All @@ -294,29 +284,35 @@ public void ImportVessel(string name)
foreach (ConfigNode.Value crewValue in partNode.values)//(string crewmember in partNode.GetValues("crew"))
{
if (crewValue.name != "crew")
{
continue;
}

string crewmember = crewValue.value;
//find the confignode saved with the vessel
ConfigNode crewNode = storedNode.GetNodes("CREW")?.FirstOrDefault(c => c.GetValue("name") == crewmember);
if (crewNode == null || crewNode.GetValue("type") != "Crew") //if no data or is tourist then remove from ship
{
//can't find the required data, so remove that kerbal from the ship
Log($"Vessel occupant is not crew: {crewmember}");
toRemove.Add(crewmember);
continue;
}


ProtoCrewMember newCrew = new ProtoCrewMember(HighLogic.CurrentGame.Mode, crewNode, ProtoCrewMember.KerbalType.Crew);
if (HighLogic.CurrentGame.CrewRoster.Crew.FirstOrDefault(c => c.name == crewmember) != null) //there's already a kerbal with that name (sadness :( )
if (HighLogic.CurrentGame.CrewRoster.Exists(crewmember)) //there's already a kerbal with that name (sadness :( )
{
//alright, rename this kerbal to a new name
string newName = RenameKerbal(crewmember);
newCrew.ChangeName(newName);
crewValue.value = newName;
}
Log($"Creating crewmember {newCrew.name}");
//add the crew member to the crew roster
//the function to do this is hidden for some reason. yay!
HighLogic.CurrentGame.CrewRoster.AddCrewMember(newCrew); //no longer hidden! MORE YAY!
crewAdded.Add(newCrew);
}
foreach (string crewmember in toRemove) //remove all crews that shouldn't be here anymore
{
Expand All @@ -325,6 +321,7 @@ public void ImportVessel(string name)
{
if (val.name == "crew" && val.value == crewmember)
{
Log($"Removing non-valid crew member {val.value}");
partNode.values.Remove(val);
break;
}
Expand All @@ -335,7 +332,7 @@ public void ImportVessel(string name)
}
catch (Exception ex)
{
Debug.Log("[ShipSaveSplicer] Encountered exception while transferring crew. The exception follows. Stripping crew data.");
Log("Encountered exception while transferring crew. The exception follows. Stripping crew data.");
Debug.LogException(ex);

StripCrew(vesselNode);
Expand All @@ -349,15 +346,19 @@ public void ImportVessel(string name)
}

ProtoVessel addedVessel = HighLogic.CurrentGame.AddVessel(vesselNode);
//we might have to assign the kerbals to the vessel here

//related issue, in 1.2.2 (at least) we fail validation of assignments when there are two ships with the same part ids (guessing).
//All I know is that if I terminate the original flight, I can import crew. Otherwise it NREs when I save.
foreach (ProtoCrewMember crew in crewAdded)
{
Log($"Assigning {crew.name}");
addedVessel.AddCrew(crew);
}
//In 1.2.2+ saving fails when there are two copies of a ship with crew onboard both. Might be part ID related.
//All I know is that if I terminate the original flight, I can import crew. Otherwise it NREs when it tries to save the flight state.
}
}

private void StripCrew(ConfigNode vesselNode)
{
Log("Stripping out crew information");
foreach (ConfigNode partNode in vesselNode.GetNodes("PART"))
{
if (partNode.HasValue("crew"))
Expand Down Expand Up @@ -395,7 +396,9 @@ public void CreateFolder()
{
string filename = KSPUtil.ApplicationRootPath + "/Ships/export/";
if (!System.IO.Directory.Exists(filename))
{
System.IO.Directory.CreateDirectory(filename);
}
}

public List<string> InvalidParts(ConfigNode vesselNode)
Expand All @@ -408,21 +411,51 @@ public List<string> InvalidParts(ConfigNode vesselNode)
{
string partName = PartNameFromNode(partNode);
if (!invalid.Contains(partName) && PartLoader.getPartInfoByName(partName) == null) //don't add duplicates
{
invalid.Add(partName);
}
}
return invalid;
}

public bool WarnOfInvalidParts(ConfigNode vesselNode, bool importing)
{
List<string> invalidParts = InvalidParts(vesselNode);
if (invalidParts.Count > 0)
{
string action = importing ? "impoerted" : "converted";
StringBuilder msg = new StringBuilder($"The selected vessel cannot be {action} because it contains the following invalid parts (perhaps you removed a mod?):").AppendLine();
foreach (string invalid in invalidParts)
{
msg.Append(" ").AppendLine(invalid);
}

PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), "missingPartsPopup", "Missing Parts", msg.ToString(), "Ok", false, HighLogic.UISkin);
return true;
}
return false;
}

public string PartNameFromNode(ConfigNode part)
{
string name = part.GetValue("part");
if (name != null)
{
name = name.Split('_')[0];
}
else
{
name = part.GetValue("name");
}

return name;
}

public void Log(object msg)
{
Debug.Log("[ShipSaveSplicer] " + msg.ToString());
}

/* The following is directly from Claw's InflightShipSave and credit goes to the original author */
private void CleanEditorNodes(ConfigNode CN)
{
Expand Down Expand Up @@ -467,11 +500,11 @@ private void PristineNodes(ConfigNode CN)
{
string PartName = ((CN.GetValue("part")).Split('_'))[0];

Debug.LogWarning("PART: " + PartName);
Log("PART: " + PartName);

Part NewPart = PartLoader.getPartInfoByName(PartName).partPrefab;
ConfigNode NewPartCN = new ConfigNode();
Debug.LogWarning("New Part: " + NewPart.name);
Log("New Part: " + NewPart.name);

NewPart.InitializeModules();

Expand Down Expand Up @@ -510,7 +543,7 @@ private void PristineNodes(ConfigNode CN)
}
}
/*
Copyright (C) 2017 Michael Marvin
Copyright (C) 2018 Michael Marvin
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
Expand Down
6 changes: 6 additions & 0 deletions ShipSaveSplicer/ShipSaveSplicer.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>"C:\Users\magico13\Desktop\KSP_Modding\Tools\pdb2mdb\pdb2mdb.exe" "$(TargetFileName)"
xcopy /Y "$(TargetPath)" "D:\SteamLibrary\SteamApps\common\Kerbal Space Program\GameData\$(TargetName)\"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "D:\SteamLibrary\SteamApps\common\Kerbal Space Program\GameData\$(TargetName)\"
xcopy /Y "$(TargetDir)$(TargetName).dll.mdb" "D:\SteamLibrary\SteamApps\common\Kerbal Space Program\GameData\$(TargetName)\"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
Expand Down

0 comments on commit 33a049a

Please sign in to comment.