-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathmoduleManager copy.cs
More file actions
379 lines (329 loc) · 12.1 KB
/
moduleManager copy.cs
File metadata and controls
379 lines (329 loc) · 12.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Text.RegularExpressions;
using UnityEngine;
using KSP;
using KSP.IO;
/* Ideal use cases
* Part.Load(ConfigNode)
* {
* this will set the Fields,
* then load all attachNodes,
* then load all Resources,
* then load all Modules.
*
* It will work whether ConfigNode is a straight PART {} node, or a @PART[name] node.
* }
*
* AvailablePart.Load(ConfigNode)
* {
* this will call Part.Load(ConfigNode)
* then reset resourceInfo
* then reset moduleInfo
* }
*
* List<ConfigNode> GameDatabase
* this will be every possible top-level ConfigNode pulled from every .cfg file in the GameData folder,
* concatenated as subnodes of one single massive ConfigNode
* This ConfigNode will also have values for every game asset
* such as mesh =
* and texture =
* and sound =
* and so on.
*
* */
namespace ModuleManager
{
public class ModuleManager : MonoBehaviour
{
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;
}
public static void ModifyPart(Part part, ConfigNode node)
{
part.Fields.Load(node);
//Step 2A: clear the old Resources
part.Resources.list.Clear ();
//Step 2B: load the new Resources
foreach(ConfigNode rNode in node.GetNodes ("RESOURCE"))
part.AddResource (rNode);
//Step 3A: clear the old Modules
while (part.Modules.Count > 0)
part.RemoveModule (part.Modules [0]);
//Step 3B: load the new Modules
foreach(ConfigNode mNode in node.GetNodes ("MODULE")) {
PartModule module = part.AddModule (mNode.GetValue ("name"));
if(module) {
// really? REALLY? It appears the only way to make this work, is to molest KSP's privates.
if(Awaken (module)) { // uses reflection to find and call the PartModule.Awake() private method
module.Load(mNode);
} else {
print ("Awaken failed for new module.");
}
if(module.part == null) {
print ("new module has null part.");
} else {
#if DEBUG
print ("Created module for " + module.part.name);
#endif
}
}
}
}
public static void ModifyPart(AvailablePart partData, ConfigNode node)
{
//Step 1: load the Fields
partData.partPrefab.Fields.Load(node);
//Step 2A: clear the old Resources
partData.partPrefab.Resources.list.Clear ();
partData.resourceInfo = "";
//Step 2B: load the new Resources
foreach(ConfigNode rNode in node.GetNodes ("RESOURCE")) {
PartResource resource = partData.partPrefab.AddResource (rNode);
if(partData.resourceInfo.Length > 0)
partData.resourceInfo += "\n";
partData.resourceInfo += resource.GetInfo ();
}
if (partData.resourceInfo.Length > 0)
partData.resourceInfo += "\nDry Mass: " + partData.partPrefab.mass.ToString ("F3");
//Step 3A: clear the old Modules
while (partData.partPrefab.Modules.Count > 0)
partData.partPrefab.RemoveModule (partData.partPrefab.Modules [0]);
partData.moduleInfo = "";
//Step 3B: load the new Modules
foreach(ConfigNode mNode in node.GetNodes ("MODULE")) {
PartModule module = partData.partPrefab.AddModule (mNode.GetValue ("name"));
if(module) {
// really? REALLY? It appears the only way to make this work, is to molest KSP's privates.
if(Awaken (module)) { // uses reflection to find and call the PartModule.Awake() private method
module.Load(mNode);
} else {
print ("Awaken failed for new module.");
}
if(module.part == null) {
print ("new module has null part.");
} else {
#if DEBUG
print ("Created module for " + module.part.name);
#endif
}
}
if(partData.moduleInfo.Length > 0)
partData.moduleInfo += "\n";
partData.moduleInfo += module.GetInfo ();
}
}
}
[KSPAddon(KSPAddon.Startup.EveryScene, true)]
public class ConfigManager : MonoBehaviour
{
public static List<AvailablePart> partDatabase = PartLoader.LoadedPartsList;
//FindConfigNodeIn finds and returns a ConfigNode in src of type nodeType. If nodeName is not null,
//it will only find a node of type nodeType with the value name=nodeName. If nodeTag is not null,
//it will only find a node of type nodeType with the value name=nodeName and tag=nodeTag.
public static ConfigNode FindConfigNodeIn(ConfigNode src, string nodeType, string nodeName, string nodeTag)
{
if (nodeTag == null)
print ("Searching node for " + nodeType + "[" + nodeName + "]");
else
print ("Searching node for " + nodeType + "[" + nodeName + "," + nodeTag + "]");
foreach (ConfigNode n in src.GetNodes (nodeType)) {
if(n.HasValue ("name") && n.GetValue ("name").Equals (nodeName) &&
(nodeTag == null ||
(n.HasValue ("tag") && n.GetValue("tag").Equals(nodeTag))) ) {
print ("found node!");
return n;
}
}
return null;
}
public static UrlDir.UrlConfig FindConfig(string nodeType, string nodeName)
{
print ("Searching " + (GameDatabase.Instance.GetConfigs (nodeType).Length+1).ToString () + nodeType + " nodes for 'name = " + nodeName + "'");
foreach (UrlDir.UrlConfig n in GameDatabase.Instance.GetConfigs(nodeType)) {
if(n.name.Equals (nodeName)) {
return n;
} else if(n.config.HasValue ("name") && n.config.GetValue ("name").Equals (nodeName)) {
return n;
}
}
return null;
}
//ModifyNode applies the ConfigNode mod as a 'patch' to ConfigNode original,
//then returns the patched ConfigNode.
// it uses FindConfigNodeIn(src, nodeType, nodeName, nodeTag) to recurse.
public static ConfigNode ModifyNode(ConfigNode original, ConfigNode mod)
{
ConfigNode newNode = new ConfigNode(original.name);
original.CopyTo (newNode);
foreach(ConfigNode.Value val in mod.values) {
if(val.name[0] == '@') {
// Modifying a value: Format is @key = value or @key,index = value
string valName = val.name.Substring (1);
int index = 0;
if(valName.Contains (",")) {
int.TryParse(valName.Split (',')[1], out index);
valName = valName.Split (',')[0];
}
newNode.SetValue (valName, val.value, index);
} else if (val.name[0] == '!') {
// Parsing: Format is @key = value or @key,index = value
string valName = val.name.Substring (1);
int index = 0;
if(valName.Contains (",")) {
int.TryParse(valName.Split (',')[1], out index);
valName = valName.Split (',')[0];
} // index is useless right now, but some day it might not be.
newNode.RemoveValue (valName);
} else {
newNode.AddValue (val.name, val.value);
}
}
foreach (ConfigNode subMod in mod.nodes) {
if(subMod.name[0] == '@') {
// Modifying a node: Format is @NODETYPE {...}, @NODETYPE[Name] {...} or @NODETYPE[Name,Tag] {...}
ConfigNode subNode = null;
if(subMod.name.Contains ("["))
{ // format @NODETYPE[Name] {...} or @NODETYPE[Name, Tag] {...}
string nodeType = subMod.name.Substring (1).Split ('[')[0].Trim ();
string nodeName = subMod.name.Split ('[')[1].Replace ("]","").Trim();
string nodeTag = null;
if(nodeName.Contains (",")) { //format @NODETYPE[Name, Tag] {...}
nodeTag = nodeName.Split (',')[1];
nodeName = nodeName.Split (',')[0];
}
subNode = FindConfigNodeIn(newNode, nodeType, nodeName, nodeTag);
} else { // format @NODETYPE {...}
string nodeType = subMod.name.Substring (1);
subNode = newNode.GetNode (nodeType);
}
// find the original subnode to modify, modify it, remove the original and add the modified.
if(subNode == null) {
print ("Could not find node to modify: " + subMod.name);
} else {
ConfigNode newSubNode = ModifyNode (subNode, subMod);
newNode.nodes.Remove (subNode);
newNode.nodes.Add (newSubNode);
}
} else if(subMod.name[0] == '!') {
// Removing a node: Format is !NODETYPE {}, !NODETYPE[Name] {} or !NODETYPE[Name,Tag] {}
ConfigNode subNode;
if(subMod.name.Contains ("["))
{ // format !NODETYPE[Name] {} or !NODETYPE[Name, Tag] {}
string nodeType = subMod.name.Substring (1).Split ('[')[0].Trim ();
string nodeName = subMod.name.Split ('[')[1].Replace ("]","").Trim();
string nodeTag = null;
if(nodeName.Contains (",")) { //format !NODETYPE[Name, Tag] {}
nodeTag = nodeName.Split (',')[1];
nodeName = nodeName.Split (',')[0];
}
subNode = FindConfigNodeIn(newNode, nodeType, nodeName, nodeTag);
} else { // format !NODETYPE {}
string nodeType = subMod.name.Substring (1);
subNode = newNode.GetNode (nodeType);
}
if(subNode != null)
newNode.nodes.Remove (subNode);
} else {
// this is a full node, not a mod, so just add it as a new subnode.
newNode.AddNode (subMod);
}
}
return newNode;
}
public static void ApplyMods(string nodeType)
{
foreach (UrlDir.UrlConfig url in GameDatabase.Instance.GetConfigs (nodeType)) {
if(!url.config.HasValue ("@modified")) // don't apply a cfg mod twice
{
string nodeName = url.name;
if (nodeType.Equals ("PART")) {
// for some reason the loader rips out all this data from the node.
// so we have to put it back.
AvailablePart partData = partDatabase.Find (p => p.name.Equals (nodeName.Replace ("_", ".")));
if(partData == null)
print ("PART[" + nodeName + "] not found!");
else {
print ("Restoring PART[" + nodeName + "] metadata");
url.config.AddValue ("name", nodeName);
url.config.AddValue ("description", partData.description);
url.config.AddValue ("category", partData.category);
url.config.AddValue ("title", partData.title);
url.config.AddValue ("module", partData.partPrefab.GetType().ToString ());
}
}
bool modded = false;
string modName = "@" + nodeType + "[" + nodeName + "]";
print ("Searching gameDatabase for " + modName);
foreach (ConfigNode mod in GameDatabase.Instance.GetConfigNodes(modName)) {
print ("Applying node " + modName);
url.config = ModifyNode (url.config, mod);
}
modName = "@" + nodeType + "[" + nodeName + "]:Final";
print ("Searching gameDatabase for " + modName);
foreach (ConfigNode mod in GameDatabase.Instance.GetConfigNodes(modName)) {
modded = true;
print ("Applying node " + modName);
url.config = ModifyNode (url.config, mod);
}
if(modded) {
url.config.AddValue ("@modified", "true");
print ("final node: " + url.config.ToString ());
} else {
url.config.AddValue ("@modified", "false");
}
}
}
}
public static List<ConfigNode> AllConfigsStartingWith(string match) {
List<ConfigNode> nodes = new List<ConfigNode>();
foreach (UrlDir.UrlConfig url in GameDatabase.Instance.root.AllConfigs) {
if(url.type.StartsWith (match))
url.config.name = url.type;
nodes.Add (url.config);
}
return nodes;
}
public bool loaded {
get {
foreach (UrlDir.UrlConfig url in GameDatabase.Instance.root.AllConfigs) {
return(url.config.HasValue ("@modified"));
}
return true;
}
}
public void OnGUI()
{
if (loaded || !GameDatabase.Instance.IsReady ())
return;
List<string> types = new List<string> ();
foreach (UrlDir.UrlConfig url in GameDatabase.Instance.root.AllConfigs) {
if (url.type [0] != '@' && !types.Contains (url.type)) {
types.Add (url.type);
print ("Applying cfg patches to type " + url.type);
ApplyMods (url.type);
}
}
// reload Resources
PartResourceLibrary.Instance.resourceDefinitions.Clear ();
PartResourceLibrary.Instance.LoadDefinitions ();
// reload Parts
if (PartLoader.LoadedPartsList.Count > 0) {
PartLoader.Instance.Recompile = true;
PartLoader.Instance.StartLoad ();
}
}
}
}