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

Commit 33a049a

Browse files
committed
Update ShipSaveSplicer to enable crew import again. Add a bunch of additional logging to SSS.
1 parent 653b18e commit 33a049a

File tree

4 files changed

+89
-49
lines changed

4 files changed

+89
-49
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,4 @@ _Pvt_Extensions/
189189
ModelManifest.xml
190190
/.vs/Modlets/v15/Server/sqlite3
191191
/.vs/slnx.sqlite
192+
/.vs/VSWorkspaceState.json

ShipSaveSplicer/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("1.1.4.0")]
36-
[assembly: AssemblyFileVersion("1.1.4.0")]
35+
[assembly: AssemblyVersion("1.2.0.0")]
36+
[assembly: AssemblyFileVersion("1.2.0.0")]

ShipSaveSplicer/ShipSaveSplicer.cs

Lines changed: 80 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,35 +11,33 @@ namespace ShipSaveSplicer
1111
[KSPAddon(KSPAddon.Startup.TrackingStation, false)]
1212
public class ShipSaveSplicer : MonoBehaviour
1313
{
14-
public static ApplicationLauncherButton theButton;
15-
public static bool includeCrew = false;
16-
private static bool EventAdded = false;
14+
private static ApplicationLauncherButton _theButton;
15+
private static bool _includeCrew = false;
16+
private static bool _eventAdded = false;
1717
public void Start()
1818
{
1919
//add button to the Stock toolbar
20-
if (!EventAdded)
20+
if (!_eventAdded)
2121
{
2222
GameEvents.onGUIApplicationLauncherReady.Add(AddButton);
23-
EventAdded = true;
23+
_eventAdded = true;
2424
}
25-
26-
//AddButton();
2725
}
2826

2927
public void OnDestroy()
3028
{
31-
if (theButton != null)
29+
if (_theButton != null)
3230
{
33-
ApplicationLauncher.Instance.RemoveModApplication(theButton);
34-
theButton = null;
31+
ApplicationLauncher.Instance.RemoveModApplication(_theButton);
32+
_theButton = null;
3533
}
3634
}
3735

3836
public void AddButton()
3937
{
40-
if (ApplicationLauncher.Ready && theButton == null)
38+
if (ApplicationLauncher.Ready && _theButton == null && HighLogic.LoadedScene == GameScenes.TRACKSTATION)
4139
{
42-
theButton = ApplicationLauncher.Instance.AddModApplication(
40+
_theButton = ApplicationLauncher.Instance.AddModApplication(
4341
OnClick,
4442
Dummy, Dummy, Dummy, Dummy, Dummy, ApplicationLauncher.AppScenes.TRACKSTATION, GameDatabase.Instance.GetTexture("ShipSaveSplicer/icon", false));
4543
}
@@ -50,22 +48,22 @@ private void Dummy() { }
5048
public void OnClick()
5149
{
5250
//figure out if mod+clicked
53-
//includeCrew = GameSettings.MODIFIER_KEY.GetKey(); //TODO: Reenable when fixed
54-
includeCrew = false;
51+
_includeCrew = GameSettings.MODIFIER_KEY.GetKey();
52+
//includeCrew = false;
5553
bool ctrlHeld = Input.GetKey(KeyCode.LeftControl);
5654

5755
if (Input.GetKey(KeyCode.LeftShift))
5856
{
5957
OpenConvertWindow(); //convert ships to craft files
60-
theButton.SetFalse();
58+
_theButton.SetFalse();
6159
return;
6260
}
6361

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

68-
if (ctrlHeld || includeCrew) //ctrl or modifier held
66+
if (ctrlHeld || _includeCrew) //ctrl or modifier held
6967
{
7068
OpenImportWindow();
7169
}
@@ -74,14 +72,16 @@ public void OnClick()
7472
ExportSelectedCraft(selectedVessel);
7573
}
7674

77-
theButton.SetFalse(false);
75+
_theButton.SetFalse(false);
7876
}
7977

8078
public void ExportSelectedCraft(Vessel vessel)
8179
{
8280
CreateFolder();
8381

8482
string filename = KSPUtil.ApplicationRootPath + "/Ships/export/" + HighLogic.SaveFolder + "_" + vessel.vesselName;
83+
Log($"Exporting vessel: {vessel.vesselName}\nExporting to file: {filename}");
84+
8585
ConfigNode nodeToSave = new ConfigNode();
8686

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

128-
public void ConvertVessel(string name)
128+
public void ConvertToCraft(string name)
129129
{
130130
if (System.IO.File.Exists(name))
131131
{
132+
Log($"Converting ship to craft file: {name}");
132133
ConfigNode storedNode = ConfigNode.Load(name);
133134

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

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

172168
try
173169
{
174170
string ShipName = VesselToSave.vesselName;
175-
// Debug.LogWarning("Saving: " + ShipName);
176171

177172
//Vessel FromFlight = FlightGlobals.Vessels.Find(v => v.id == VesselToSave.vesselID);
178173
try
@@ -182,7 +177,7 @@ public void VesselToCraftFile(ConfigNode VesselNode)
182177
catch (Exception ex)
183178
{
184179
Debug.LogException(ex);
185-
Debug.Log("Attempting to continue.");
180+
Log("Attempting to continue.");
186181
}
187182

188183
ShipConstruct ConstructToSave = new ShipConstruct(ShipName, "", VesselToSave.vesselRef.Parts[0]);
@@ -247,8 +242,7 @@ public void OpenImportWindow()
247242
options[i] = new DialogGUIButton(files[i].Split('/').Last(), () => { ImportVessel(files[select]); });
248243
}
249244
options[count] = new DialogGUIButton("Close", Dummy);
250-
string msg = "Select a vessel to import. Will " + (includeCrew ? "" : "not ") + "import crew members.";// +(includeCrew ? "": " (modifier+click the SSS button to include crew)");
251-
//TODO: Reenable when fixed
245+
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)"));
252246

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

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

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

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

267+
List<ProtoCrewMember> crewAdded = new List<ProtoCrewMember>();
277268

278-
279-
if (!includeCrew)
269+
if (!_includeCrew)
280270
{
281271
//clear out all crew on vessel
282272
StripCrew(vesselNode);
@@ -294,29 +284,35 @@ public void ImportVessel(string name)
294284
foreach (ConfigNode.Value crewValue in partNode.values)//(string crewmember in partNode.GetValues("crew"))
295285
{
296286
if (crewValue.name != "crew")
287+
{
297288
continue;
289+
}
290+
298291
string crewmember = crewValue.value;
299292
//find the confignode saved with the vessel
300293
ConfigNode crewNode = storedNode.GetNodes("CREW")?.FirstOrDefault(c => c.GetValue("name") == crewmember);
301294
if (crewNode == null || crewNode.GetValue("type") != "Crew") //if no data or is tourist then remove from ship
302295
{
303296
//can't find the required data, so remove that kerbal from the ship
297+
Log($"Vessel occupant is not crew: {crewmember}");
304298
toRemove.Add(crewmember);
305299
continue;
306300
}
307301

308302

309303
ProtoCrewMember newCrew = new ProtoCrewMember(HighLogic.CurrentGame.Mode, crewNode, ProtoCrewMember.KerbalType.Crew);
310-
if (HighLogic.CurrentGame.CrewRoster.Crew.FirstOrDefault(c => c.name == crewmember) != null) //there's already a kerbal with that name (sadness :( )
304+
if (HighLogic.CurrentGame.CrewRoster.Exists(crewmember)) //there's already a kerbal with that name (sadness :( )
311305
{
312306
//alright, rename this kerbal to a new name
313307
string newName = RenameKerbal(crewmember);
314308
newCrew.ChangeName(newName);
315309
crewValue.value = newName;
316310
}
311+
Log($"Creating crewmember {newCrew.name}");
317312
//add the crew member to the crew roster
318313
//the function to do this is hidden for some reason. yay!
319314
HighLogic.CurrentGame.CrewRoster.AddCrewMember(newCrew); //no longer hidden! MORE YAY!
315+
crewAdded.Add(newCrew);
320316
}
321317
foreach (string crewmember in toRemove) //remove all crews that shouldn't be here anymore
322318
{
@@ -325,6 +321,7 @@ public void ImportVessel(string name)
325321
{
326322
if (val.name == "crew" && val.value == crewmember)
327323
{
324+
Log($"Removing non-valid crew member {val.value}");
328325
partNode.values.Remove(val);
329326
break;
330327
}
@@ -335,7 +332,7 @@ public void ImportVessel(string name)
335332
}
336333
catch (Exception ex)
337334
{
338-
Debug.Log("[ShipSaveSplicer] Encountered exception while transferring crew. The exception follows. Stripping crew data.");
335+
Log("Encountered exception while transferring crew. The exception follows. Stripping crew data.");
339336
Debug.LogException(ex);
340337

341338
StripCrew(vesselNode);
@@ -349,15 +346,19 @@ public void ImportVessel(string name)
349346
}
350347

351348
ProtoVessel addedVessel = HighLogic.CurrentGame.AddVessel(vesselNode);
352-
//we might have to assign the kerbals to the vessel here
353-
354-
//related issue, in 1.2.2 (at least) we fail validation of assignments when there are two ships with the same part ids (guessing).
355-
//All I know is that if I terminate the original flight, I can import crew. Otherwise it NREs when I save.
349+
foreach (ProtoCrewMember crew in crewAdded)
350+
{
351+
Log($"Assigning {crew.name}");
352+
addedVessel.AddCrew(crew);
353+
}
354+
//In 1.2.2+ saving fails when there are two copies of a ship with crew onboard both. Might be part ID related.
355+
//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.
356356
}
357357
}
358358

359359
private void StripCrew(ConfigNode vesselNode)
360360
{
361+
Log("Stripping out crew information");
361362
foreach (ConfigNode partNode in vesselNode.GetNodes("PART"))
362363
{
363364
if (partNode.HasValue("crew"))
@@ -395,7 +396,9 @@ public void CreateFolder()
395396
{
396397
string filename = KSPUtil.ApplicationRootPath + "/Ships/export/";
397398
if (!System.IO.Directory.Exists(filename))
399+
{
398400
System.IO.Directory.CreateDirectory(filename);
401+
}
399402
}
400403

401404
public List<string> InvalidParts(ConfigNode vesselNode)
@@ -408,21 +411,51 @@ public List<string> InvalidParts(ConfigNode vesselNode)
408411
{
409412
string partName = PartNameFromNode(partNode);
410413
if (!invalid.Contains(partName) && PartLoader.getPartInfoByName(partName) == null) //don't add duplicates
414+
{
411415
invalid.Add(partName);
416+
}
412417
}
413418
return invalid;
414419
}
415420

421+
public bool WarnOfInvalidParts(ConfigNode vesselNode, bool importing)
422+
{
423+
List<string> invalidParts = InvalidParts(vesselNode);
424+
if (invalidParts.Count > 0)
425+
{
426+
string action = importing ? "impoerted" : "converted";
427+
StringBuilder msg = new StringBuilder($"The selected vessel cannot be {action} because it contains the following invalid parts (perhaps you removed a mod?):").AppendLine();
428+
foreach (string invalid in invalidParts)
429+
{
430+
msg.Append(" ").AppendLine(invalid);
431+
}
432+
433+
PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), "missingPartsPopup", "Missing Parts", msg.ToString(), "Ok", false, HighLogic.UISkin);
434+
return true;
435+
}
436+
return false;
437+
}
438+
416439
public string PartNameFromNode(ConfigNode part)
417440
{
418441
string name = part.GetValue("part");
419442
if (name != null)
443+
{
420444
name = name.Split('_')[0];
445+
}
421446
else
447+
{
422448
name = part.GetValue("name");
449+
}
450+
423451
return name;
424452
}
425453

454+
public void Log(object msg)
455+
{
456+
Debug.Log("[ShipSaveSplicer] " + msg.ToString());
457+
}
458+
426459
/* The following is directly from Claw's InflightShipSave and credit goes to the original author */
427460
private void CleanEditorNodes(ConfigNode CN)
428461
{
@@ -467,11 +500,11 @@ private void PristineNodes(ConfigNode CN)
467500
{
468501
string PartName = ((CN.GetValue("part")).Split('_'))[0];
469502

470-
Debug.LogWarning("PART: " + PartName);
503+
Log("PART: " + PartName);
471504

472505
Part NewPart = PartLoader.getPartInfoByName(PartName).partPrefab;
473506
ConfigNode NewPartCN = new ConfigNode();
474-
Debug.LogWarning("New Part: " + NewPart.name);
507+
Log("New Part: " + NewPart.name);
475508

476509
NewPart.InitializeModules();
477510

@@ -510,7 +543,7 @@ private void PristineNodes(ConfigNode CN)
510543
}
511544
}
512545
/*
513-
Copyright (C) 2017 Michael Marvin
546+
Copyright (C) 2018 Michael Marvin
514547
515548
This program is free software: you can redistribute it and/or modify
516549
it under the terms of the GNU General Public License as published by

ShipSaveSplicer/ShipSaveSplicer.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,12 @@
5353
<Compile Include="Properties\AssemblyInfo.cs" />
5454
</ItemGroup>
5555
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
56+
<PropertyGroup>
57+
<PostBuildEvent>"C:\Users\magico13\Desktop\KSP_Modding\Tools\pdb2mdb\pdb2mdb.exe" "$(TargetFileName)"
58+
xcopy /Y "$(TargetPath)" "D:\SteamLibrary\SteamApps\common\Kerbal Space Program\GameData\$(TargetName)\"
59+
xcopy /Y "$(TargetDir)$(TargetName).pdb" "D:\SteamLibrary\SteamApps\common\Kerbal Space Program\GameData\$(TargetName)\"
60+
xcopy /Y "$(TargetDir)$(TargetName).dll.mdb" "D:\SteamLibrary\SteamApps\common\Kerbal Space Program\GameData\$(TargetName)\"</PostBuildEvent>
61+
</PropertyGroup>
5662
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
5763
Other similar extension points exist, see Microsoft.Common.targets.
5864
<Target Name="BeforeBuild">

0 commit comments

Comments
 (0)