@@ -45,12 +45,20 @@ orderby ass.assembly.GetName().Version descending, ass.path ascending
45
45
return true ;
46
46
}
47
47
48
+ private static bool hasRun = false ;
49
+
48
50
internal void Awake ( )
49
51
{
50
52
try
51
53
{
52
54
if ( ! RunTypeElection ( typeof ( SaveGameFixer ) , "ModuleManager" ) )
53
55
return ;
56
+
57
+ // Guard against multiple copies of the same DLL
58
+ if ( hasRun )
59
+ return ;
60
+ hasRun = true ;
61
+
54
62
// So at this point we know we have won the election, and will be using the class versions as in this assembly.
55
63
56
64
UpdateSaves ( ) ;
@@ -68,15 +76,42 @@ internal void Awake()
68
76
}
69
77
#endregion
70
78
71
- #region Finding the part
79
+ #region State
80
+
81
+ // Files and directories
72
82
private string savesRoot ;
83
+ private string backupDir = null ;
84
+ private string logFile = null ;
85
+
86
+ // Bits and pieces for logging
87
+ private StringBuilder backupLog = new StringBuilder ( ) ;
88
+ private List < string > logContext = new List < string > ( ) ;
89
+ private int logCtxCur = 0 ;
90
+
91
+ // Flags
92
+ private bool logOnly = true ;
93
+ private bool needsBackup = false ;
94
+ private bool needsSave = false ;
95
+ private bool partMissing = false ;
96
+
97
+ #endregion
98
+
99
+ #region Finding the part
100
+
73
101
74
102
private void UpdateSaves ( )
75
103
{
76
104
savesRoot = Path . Combine ( Path . GetFullPath ( KSPUtil . ApplicationRootPath ) , "saves" + Path . DirectorySeparatorChar ) ;
77
105
78
106
foreach ( string saveDir in Directory . GetDirectories ( savesRoot ) )
79
107
UpdateSaveDir ( saveDir ) ;
108
+
109
+ // Write the backup log if needed
110
+ if ( ! logOnly && backupLog . Length > 0 )
111
+ {
112
+ CreateBackupDir ( ) ;
113
+ File . AppendAllText ( logFile , backupLog . ToString ( ) ) ;
114
+ }
80
115
}
81
116
82
117
@@ -85,7 +120,7 @@ private void UpdateSaveDir(string saveDir)
85
120
try
86
121
{
87
122
PushLogContext ( "Save Game: " + saveDir . Substring ( savesRoot . Length , saveDir . Length - savesRoot . Length ) ) ;
88
-
123
+
89
124
char ds = Path . DirectorySeparatorChar ;
90
125
91
126
// .craft files
@@ -126,11 +161,21 @@ private void UpdateCraft(string vabCraft)
126
161
PushLogContext ( "Craft file: " + vabCraft . Substring ( savesRoot . Length , vabCraft . Length - savesRoot . Length ) ) ;
127
162
ConfigNode craft = ConfigNode . Load ( vabCraft ) ;
128
163
129
- bool needsBackup = false , needsSave = false ;
164
+ needsBackup = false ; needsSave = false ; partMissing = false ;
165
+
130
166
foreach ( ConfigNode part in craft . GetNodes ( "PART" ) )
131
- UpdatePart ( part , ref needsBackup , ref needsSave ) ;
167
+ UpdatePart ( part ) ;
168
+
169
+ // If a part is missing don't do anything special. The game just locks the craft, it doesn't destory them.
170
+ if ( partMissing )
171
+ {
172
+ WriteDebugMessage ( "Craft has mising parts in the VAB, the craft file will be locked." ) ;
173
+ WriteDebugMessage ( "Delete the craft to get rid of this message." ) ;
174
+ }
175
+
176
+ BackupAndReplace ( vabCraft , craft ) ;
177
+
132
178
133
- BackupAndReplace ( vabCraft , craft , needsBackup , needsSave ) ;
134
179
}
135
180
finally
136
181
{
@@ -141,18 +186,28 @@ private void UpdateCraft(string vabCraft)
141
186
private void UpdateSFS ( string sfsFile )
142
187
{
143
188
ConfigNode sfs = ConfigNode . Load ( sfsFile ) ;
144
-
145
189
try
146
190
{
147
191
PushLogContext ( "Save file: " + sfsFile . Substring ( savesRoot . Length , sfsFile . Length - savesRoot . Length ) ) ;
148
192
149
- bool needsBackup = false , needsSave = false ;
193
+ needsBackup = false ; needsSave = false ; partMissing = false ;
194
+
150
195
foreach ( ConfigNode game in sfs . GetNodes ( "GAME" ) )
151
196
foreach ( ConfigNode flightState in game . GetNodes ( "FLIGHTSTATE" ) )
152
197
foreach ( ConfigNode vessel in flightState . GetNodes ( "VESSEL" ) )
153
- UpdateVessel ( vessel , ref needsBackup , ref needsSave ) ;
198
+ UpdateVessel ( vessel ) ;
154
199
155
- BackupAndReplace ( sfsFile , sfs , needsBackup , needsSave ) ;
200
+ // Backup if missing parts.
201
+ // TODO: handle missing parts more gracefully, like missing modules are handled.
202
+ if ( partMissing )
203
+ {
204
+ WriteLogMessage ( "Save game has vessels with missing parts. These vessels will be deleted on loading the save." ) ;
205
+ WriteLogMessage ( "The persistence file has been backed up. Note that this will keep occuring every load until" ) ;
206
+ WriteLogMessage ( "either the save game is loaded and the ships are destroyed, or the missing parts are not missing." ) ;
207
+ needsBackup = true ;
208
+ }
209
+
210
+ BackupAndReplace ( sfsFile , sfs ) ;
156
211
}
157
212
finally
158
213
{
@@ -161,14 +216,14 @@ private void UpdateSFS(string sfsFile)
161
216
162
217
}
163
218
164
- private void UpdateVessel ( ConfigNode vessel , ref bool needsBackup , ref bool needsSave )
219
+ private void UpdateVessel ( ConfigNode vessel )
165
220
{
166
221
try
167
222
{
168
223
PushLogContext ( "Vessel: " + vessel . GetValue ( "name" ) ) ;
169
224
170
225
foreach ( ConfigNode part in vessel . GetNodes ( "PART" ) )
171
- UpdatePart ( part , ref needsBackup , ref needsSave ) ;
226
+ UpdatePart ( part ) ;
172
227
}
173
228
finally
174
229
{
@@ -177,7 +232,7 @@ private void UpdateVessel(ConfigNode vessel, ref bool needsBackup, ref bool need
177
232
}
178
233
#endregion
179
234
180
- private void UpdatePart ( ConfigNode part , ref bool needsBackup , ref bool needsSave )
235
+ private void UpdatePart ( ConfigNode part )
181
236
{
182
237
// The modules saved with the part
183
238
ConfigNode [ ] savedModules = part . GetNodes ( "MODULE" ) ;
@@ -214,8 +269,9 @@ private void UpdatePart(ConfigNode part, ref bool needsBackup, ref bool needsSav
214
269
215
270
if ( available == null )
216
271
{
217
- WriteLogMessage ( "Backup created - part \" " + partName + "\" has been deleted and ship will be destroyed." ) ;
218
- needsBackup = true ;
272
+ WriteLogMessage ( "Part \" " + partName + "\" has been deleted." ) ;
273
+ partMissing = true ;
274
+ return ;
219
275
}
220
276
221
277
PartModuleList prefabModules = available . partPrefab . Modules ;
@@ -232,20 +288,7 @@ private void UpdatePart(ConfigNode part, ref bool needsBackup, ref bool needsSav
232
288
return ;
233
289
needUpdate : ;
234
290
}
235
-
236
291
// Yes we do!
237
- #if false
238
- string prefabNames = "Prefab modules: " ;
239
- for ( int i = 0 ; i < prefabModules . Count ; ++ i )
240
- prefabNames += ( prefabModules [ i ] as PartModule ) . moduleName + "," ;
241
- prefabNames = prefabNames . Substring ( 0 , prefabNames . Length - 1 ) ;
242
-
243
- Debug . Log ( "[SaveGameFixer] Fixing Part: " + partName + " in file: " + source + "\n " + prefabNames
244
- + "\n Saved modules: " + string . Join ( "," , ( from s in savedModules select ( s == null ? "***" : s . GetValue ( "name" ) ) ) . ToArray ( ) )
245
- + "\n Backup modules: " + string . Join ( "," , ( from s in backupModules select ( s == null ? "***" : s . GetValue ( "name" ) ) ) . ToArray ( ) )
246
- //+ "\nConfig: \n" + part
247
- ) ;
248
- #endif
249
292
250
293
// Discard any backups that are already in saved modules
251
294
for ( int i = 0 ; i < backupModules . Length ; ++ i )
@@ -373,11 +416,6 @@ private void UpdatePart(ConfigNode part, ref bool needsBackup, ref bool needsSav
373
416
374
417
#region Backups
375
418
376
- private List < string > logContext = new List < string > ( ) ;
377
- private int logCtxCur = 0 ;
378
- private string backupDir = null ;
379
- private string logFile = null ;
380
-
381
419
private void PushLogContext ( string p )
382
420
{
383
421
logContext . Add ( p ) ;
@@ -397,29 +435,20 @@ private void WriteDebugMessage(string logMessage)
397
435
398
436
private void WriteLogMessage ( string logMessage , bool debugMsg = false )
399
437
{
400
- #if DEBUG
401
- string dbg = debugMsg ? "[dbg]" : "[log]" ;
402
-
403
- #else
404
- string dbg = string . Empty ;
405
- #endif
406
- CreateBackupDir ( ) ;
407
-
408
- StringBuilder sb = new StringBuilder ( ) ;
409
-
410
438
// Write any pending log headers
411
439
string indent ;
412
440
for ( ; logCtxCur < logContext . Count ; logCtxCur ++ )
413
441
{
414
- indent = new String ( ' ' , 4 * logCtxCur + dbg . Length ) ;
415
- sb . Append ( indent ) . AppendLine ( logContext [ logCtxCur ] ) ;
416
- Debug . Log ( "[SaveGameFixer]" + indent + logContext [ logCtxCur ] ) ;
442
+ indent = new String ( ' ' , 4 * logCtxCur ) ;
443
+ backupLog . Append ( ' ' ) . Append ( indent ) . AppendLine ( logContext [ logCtxCur ] ) ;
444
+ Debug . Log ( indent + logContext [ logCtxCur ] ) ;
417
445
}
418
446
indent = new String ( ' ' , 4 * logCtxCur ) ;
419
- sb . Append ( dbg ) . Append ( indent ) . AppendLine ( logMessage ) ;
420
- Debug . Log ( "[SaveGameFixer]" + dbg + indent + logMessage ) ;
421
-
422
- File . AppendAllText ( logFile , sb . ToString ( ) ) ;
447
+ backupLog . Append ( debugMsg ? ' ' : '*' ) . Append ( indent ) . AppendLine ( logMessage ) ;
448
+ if ( debugMsg )
449
+ Debug . Log ( indent + logMessage ) ;
450
+ else
451
+ Debug . LogWarning ( indent + logMessage ) ;
423
452
}
424
453
425
454
private void CreateBackupDir ( )
@@ -432,11 +461,10 @@ private void CreateBackupDir()
432
461
}
433
462
}
434
463
435
- private void BackupAndReplace ( string file , ConfigNode config , bool needsBackup , bool needsSave )
464
+ private void BackupAndReplace ( string file , ConfigNode config )
436
465
{
437
- #if ! DEBUG
466
+
438
467
if ( needsBackup )
439
- #endif
440
468
{
441
469
CreateBackupDir ( ) ;
442
470
@@ -446,13 +474,14 @@ private void BackupAndReplace(string file, ConfigNode config, bool needsBackup,
446
474
Directory . CreateDirectory ( Path . GetDirectoryName ( backupTo ) ) ;
447
475
448
476
File . Copy ( file , backupTo ) ;
477
+
478
+ logOnly = false ;
449
479
}
450
480
451
481
if ( needsSave )
452
482
config . Save ( file ) ;
453
483
}
454
484
455
-
456
485
#endregion
457
486
}
458
487
0 commit comments