Skip to content

Commit 48df502

Browse files
committed
Operate on a copy of the game database then apply
* Insert nodes are now patches. They don't support MM syntax yet (just applied directly) but that could be added * ProtoUrlConfig identifies a UrlFile and node without the expectation that the UrlFile knows about the node (turned into a real UrlConfig at the end) * Intermedate state of the game database is now a linked list of nodes
1 parent e1a2be8 commit 48df502

26 files changed

Lines changed: 732 additions & 412 deletions

ModuleManager/MMPatchLoader.cs

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -167,9 +167,9 @@ private IEnumerator ProcessPatch()
167167
PatchExtractor extractor = new PatchExtractor(progress, logger, needsChecker, tagListParser, protoPatchBuilder, patchCompiler);
168168

169169
// Have to convert to an array because we will be removing patches
170-
UrlDir.UrlConfig[] allConfigs = GameDatabase.Instance.root.AllConfigs.ToArray();
171-
IEnumerable<IPatch> extractedPatches = allConfigs.Select(urlConfig => extractor.ExtractPatch(urlConfig));
172-
PatchList patchList = new PatchList(mods, extractedPatches.Where(patch => patch != null), progress);
170+
IEnumerable<IPatch> extractedPatches =
171+
GameDatabase.Instance.root.AllConfigs.Select(urlConfig => extractor.ExtractPatch(urlConfig)).Where(patch => patch != null);
172+
PatchList patchList = new PatchList(mods, extractedPatches, progress);
173173

174174
#endregion
175175

@@ -187,9 +187,11 @@ private IEnumerator ProcessPatch()
187187

188188
logger.Info("Starting patch thread");
189189

190+
IEnumerable<IProtoUrlConfig> databaseConfigs = null;
191+
190192
ITaskStatus patchThread = BackgroundTask.Start(delegate
191193
{
192-
applier.ApplyPatches(GameDatabase.Instance.root.AllConfigFiles.ToArray(), patchList);
194+
databaseConfigs = applier.ApplyPatches(patchList);
193195
});
194196

195197
float nextYield = Time.realtimeSinceStartup + yieldInterval;
@@ -236,6 +238,22 @@ float updateTimeRemaining()
236238
FatalErrorHandler.HandleFatalError("The patch runner threw an exception");
237239
yield break;
238240
}
241+
if (databaseConfigs == null)
242+
{
243+
progress.Error("The patcher returned a null collection of configs");
244+
FatalErrorHandler.HandleFatalError("The patcher returned a null collection of configs");
245+
yield break;
246+
}
247+
248+
foreach (UrlDir.UrlFile file in GameDatabase.Instance.root.AllConfigFiles)
249+
{
250+
file.configs.Clear();
251+
}
252+
253+
foreach (IProtoUrlConfig protoConfig in databaseConfigs)
254+
{
255+
protoConfig.UrlFile.AddConfig(protoConfig.Node);
256+
}
239257

240258
logger.Info("Done patching");
241259
yield return null;
@@ -1219,28 +1237,24 @@ private static ConfigNode RecurseNodeSearch(string path, NodeStack nodeStack, Pa
12191237
// @XXXXX
12201238
if (root)
12211239
{
1222-
IEnumerable<UrlDir.UrlConfig> urlConfigs = context.databaseRoot.GetConfigs(nodeType);
1223-
if (!urlConfigs.Any())
1240+
bool foundNodeType = false;
1241+
foreach (IProtoUrlConfig urlConfig in context.databaseConfigs)
12241242
{
1225-
context.logger.Warning("Can't find nodeType:" + nodeType);
1226-
return null;
1227-
}
1243+
ConfigNode node = urlConfig.Node;
12281244

1229-
if (nodeName == null)
1230-
{
1231-
nodeStack = new NodeStack(urlConfigs.First().config);
1232-
}
1233-
else
1234-
{
1235-
foreach (UrlDir.UrlConfig url in urlConfigs)
1245+
if (node.name != nodeType) continue;
1246+
1247+
foundNodeType = true;
1248+
1249+
if (nodeName == null || (node.GetValue("name") is string testNodeName && WildcardMatch(testNodeName, nodeName)))
12361250
{
1237-
if (url.config.HasValue("name") && WildcardMatch(url.config.GetValue("name"), nodeName))
1238-
{
1239-
nodeStack = new NodeStack(url.config);
1240-
break;
1241-
}
1251+
nodeStack = new NodeStack(node);
1252+
break;
12421253
}
12431254
}
1255+
1256+
if (!foundNodeType) context.logger.Warning("Can't find nodeType:" + nodeType);
1257+
if (nodeStack == null) return null;
12441258
}
12451259
else
12461260
{
@@ -1305,7 +1319,6 @@ private static ConfigNode.Value RecurseVariableSearch(string path, NodeStack nod
13051319

13061320
string subName = path.Substring(1, nextSep - 1);
13071321
string nodeType, nodeName;
1308-
UrlDir.UrlConfig target = null;
13091322

13101323
if (subName.Contains("["))
13111324
{
@@ -1317,32 +1330,27 @@ private static ConfigNode.Value RecurseVariableSearch(string path, NodeStack nod
13171330
{
13181331
// @NODETYPE/
13191332
nodeType = subName;
1320-
nodeName = string.Empty;
1333+
nodeName = null;
13211334
}
13221335

1323-
IEnumerable<UrlDir.UrlConfig> urlConfigs = context.databaseRoot.GetConfigs(nodeType);
1324-
if (!urlConfigs.Any())
1336+
bool foundNodeType = false;
1337+
foreach (IProtoUrlConfig urlConfig in context.databaseConfigs)
13251338
{
1326-
context.logger.Warning("Can't find nodeType:" + nodeType);
1327-
return null;
1328-
}
1339+
ConfigNode node = urlConfig.Node;
13291340

1330-
if (nodeName == string.Empty)
1331-
{
1332-
target = urlConfigs.First();
1333-
}
1334-
else
1335-
{
1336-
foreach (UrlDir.UrlConfig url in urlConfigs)
1341+
if (node.name != nodeType) continue;
1342+
1343+
foundNodeType = true;
1344+
1345+
if (nodeName == null || (node.GetValue("name") is string testNodeName && WildcardMatch(testNodeName, nodeName)))
13371346
{
1338-
if (url.config.HasValue("name") && WildcardMatch(url.config.GetValue("name"), nodeName))
1339-
{
1340-
target = url;
1341-
break;
1342-
}
1347+
return RecurseVariableSearch(path.Substring(nextSep + 1), new NodeStack(node), context);
13431348
}
13441349
}
1345-
return target != null ? RecurseVariableSearch(path.Substring(nextSep + 1), new NodeStack(target.config), context) : null;
1350+
1351+
if (!foundNodeType) context.logger.Warning("Can't find nodeType:" + nodeType);
1352+
1353+
return null;
13461354
}
13471355
if (path.StartsWith("../"))
13481356
{

ModuleManager/ModuleManager.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
<Compile Include="Pass.cs" />
6868
<Compile Include="PatchApplier.cs" />
6969
<Compile Include="PatchContext.cs" />
70+
<Compile Include="Patches\InsertPatch.cs" />
7071
<Compile Include="Patches\PassSpecifiers\AfterPassSpecifier.cs" />
7172
<Compile Include="Patches\PassSpecifiers\BeforePassSpecifier.cs" />
7273
<Compile Include="Patches\PassSpecifiers\FinalPassSpecifier.cs" />
@@ -90,6 +91,7 @@
9091
<Compile Include="Properties\Resources.Designer.cs" />
9192
<Compile Include="Progress\IPatchProgress.cs" />
9293
<Compile Include="Progress\PatchProgress.cs" />
94+
<Compile Include="ProtoUrlConfig.cs" />
9395
<Compile Include="Tags\Tag.cs" />
9496
<Compile Include="Tags\TagList.cs" />
9597
<Compile Include="Tags\TagListParser.cs" />

ModuleManager/PatchApplier.cs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using System;
22
using System.Collections.Generic;
3-
using System.Linq;
43
using ModuleManager.Logging;
54
using ModuleManager.Extensions;
65
using ModuleManager.Patches;
@@ -21,17 +20,21 @@ public PatchApplier(IPatchProgress progress, IBasicLogger logger)
2120
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
2221
}
2322

24-
public void ApplyPatches(IEnumerable<UrlDir.UrlFile> configFiles, IEnumerable<IPass> patches)
23+
public IEnumerable<IProtoUrlConfig> ApplyPatches(IEnumerable<IPass> patches)
2524
{
26-
if (configFiles == null) throw new ArgumentNullException(nameof(configFiles));
2725
if (patches == null) throw new ArgumentNullException(nameof(patches));
26+
27+
LinkedList<IProtoUrlConfig> databaseConfigs = new LinkedList<IProtoUrlConfig>();
28+
2829
foreach (IPass pass in patches)
2930
{
30-
ApplyPatches(configFiles, pass);
31+
ApplyPatches(databaseConfigs, pass);
3132
}
33+
34+
return databaseConfigs;
3235
}
3336

34-
private void ApplyPatches(IEnumerable<UrlDir.UrlFile> configFiles, IPass pass)
37+
private void ApplyPatches(LinkedList<IProtoUrlConfig> databaseConfigs, IPass pass)
3538
{
3639
logger.Info(pass.Name + " pass");
3740
Activity = "ModuleManager " + pass.Name;
@@ -40,10 +43,7 @@ private void ApplyPatches(IEnumerable<UrlDir.UrlFile> configFiles, IPass pass)
4043
{
4144
try
4245
{
43-
foreach (UrlDir.UrlFile file in configFiles)
44-
{
45-
patch.Apply(file, progress, logger);
46-
}
46+
patch.Apply(databaseConfigs, progress, logger);
4747
progress.PatchApplied();
4848
}
4949
catch (Exception e)

ModuleManager/PatchContext.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using ModuleManager.Logging;
34
using ModuleManager.Progress;
45

@@ -7,14 +8,14 @@ namespace ModuleManager
78
public struct PatchContext
89
{
910
public readonly UrlDir.UrlConfig patchUrl;
10-
public readonly UrlDir databaseRoot;
11+
public readonly IEnumerable<IProtoUrlConfig> databaseConfigs;
1112
public readonly IBasicLogger logger;
1213
public readonly IPatchProgress progress;
1314

14-
public PatchContext(UrlDir.UrlConfig patchUrl, UrlDir databaseRoot, IBasicLogger logger, IPatchProgress progress)
15+
public PatchContext(UrlDir.UrlConfig patchUrl, IEnumerable<IProtoUrlConfig> databaseConfigs, IBasicLogger logger, IPatchProgress progress)
1516
{
1617
this.patchUrl = patchUrl;
17-
this.databaseRoot = databaseRoot;
18+
this.databaseConfigs = databaseConfigs;
1819
this.logger = logger;
1920
this.progress = progress;
2021
}

ModuleManager/PatchExtractor.cs

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,6 @@ public IPatch ExtractPatch(UrlDir.UrlConfig urlConfig)
3939

4040
try
4141
{
42-
int index = urlConfig.parent.configs.IndexOf(urlConfig);
43-
urlConfig.parent.configs.RemoveAt(index);
44-
4542
if (!urlConfig.type.IsBracketBalanced())
4643
{
4744
progress.Error(urlConfig, "Error - node name does not have balanced brackets (or a space - if so replace with ?):\n" + urlConfig.SafeUrl());
@@ -103,21 +100,9 @@ public IPatch ExtractPatch(UrlDir.UrlConfig urlConfig)
103100
{
104101
return null;
105102
}
106-
107-
if (command == Command.Insert)
108-
{
109-
ConfigNode newNode = urlConfig.config.DeepCopy();
110-
newNode.name = protoPatch.nodeType;
111-
newNode.id = urlConfig.config.id;
112-
needsChecker.CheckNeedsRecursive(newNode, urlConfig);
113-
urlConfig.parent.configs.Insert(index, new UrlDir.UrlConfig(urlConfig.parent, newNode));
114-
return null;
115-
}
116-
else
117-
{
118-
needsChecker.CheckNeedsRecursive(urlConfig.config, urlConfig);
119-
return patchCompiler.CompilePatch(protoPatch);
120-
}
103+
104+
needsChecker.CheckNeedsRecursive(urlConfig.config, urlConfig);
105+
return patchCompiler.CompilePatch(protoPatch);
121106
}
122107
catch(Exception e)
123108
{

ModuleManager/PatchList.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public ModPassCollection(IEnumerable<string> modList)
7070
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
7171
}
7272

73+
private readonly Pass insertPatches = new Pass(":INSERT (initial)");
7374
private readonly Pass firstPatches = new Pass(":FIRST");
7475
private readonly Pass legacyPatches = new Pass(":LEGACY (default)");
7576
private readonly Pass finalPatches = new Pass(":FINAL");
@@ -84,7 +85,11 @@ public PatchList(IEnumerable<string> modList, IEnumerable<IPatch> patches, IPatc
8485

8586
foreach (IPatch patch in patches)
8687
{
87-
if (patch.PassSpecifier is FirstPassSpecifier)
88+
if (patch.PassSpecifier is InsertPassSpecifier)
89+
{
90+
insertPatches.Add(patch);
91+
}
92+
else if (patch.PassSpecifier is FirstPassSpecifier)
8893
{
8994
firstPatches.Add(patch);
9095
}
@@ -127,6 +132,7 @@ public PatchList(IEnumerable<string> modList, IEnumerable<IPatch> patches, IPatc
127132

128133
public IEnumerator<IPass> GetEnumerator()
129134
{
135+
yield return insertPatches;
130136
yield return firstPatches;
131137
yield return legacyPatches;
132138

ModuleManager/Patches/CopyPatch.cs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using NodeStack = ModuleManager.Collections.ImmutableStack<ConfigNode>;
34
using ModuleManager.Extensions;
45
using ModuleManager.Logging;
@@ -20,37 +21,35 @@ public CopyPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpec
2021
PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier));
2122
}
2223

23-
public void Apply(UrlDir.UrlFile file, IPatchProgress progress, IBasicLogger logger)
24+
public void Apply(LinkedList<IProtoUrlConfig> databaseConfigs, IPatchProgress progress, IBasicLogger logger)
2425
{
25-
if (file == null) throw new ArgumentNullException(nameof(file));
26+
if (databaseConfigs == null) throw new ArgumentNullException(nameof(databaseConfigs));
2627
if (progress == null) throw new ArgumentNullException(nameof(progress));
2728
if (logger == null) throw new ArgumentNullException(nameof(logger));
2829

29-
PatchContext context = new PatchContext(UrlConfig, file.root, logger, progress);
30+
PatchContext context = new PatchContext(UrlConfig, databaseConfigs, logger, progress);
3031

31-
// Avoid checking the new configs we are creating
32-
int count = file.configs.Count;
33-
for (int i = 0; i < count; i++)
32+
for (LinkedListNode<IProtoUrlConfig> listNode = databaseConfigs.First; listNode != null; listNode = listNode.Next)
3433
{
35-
UrlDir.UrlConfig url = file.configs[i];
34+
IProtoUrlConfig protoConfig = listNode.Value;
3635
try
3736
{
38-
if (!NodeMatcher.IsMatch(url.config)) continue;
37+
if (!NodeMatcher.IsMatch(protoConfig.Node)) continue;
3938

40-
ConfigNode clone = MMPatchLoader.ModifyNode(new NodeStack(url.config), UrlConfig.config, context);
41-
if (url.config.HasValue("name") && url.config.GetValue("name") == clone.GetValue("name"))
39+
ConfigNode clone = MMPatchLoader.ModifyNode(new NodeStack(protoConfig.Node), UrlConfig.config, context);
40+
if (protoConfig.Node.GetValue("name") is string name && name == clone.GetValue("name"))
4241
{
43-
progress.Error(UrlConfig, $"Error - when applying copy {UrlConfig.SafeUrl()} to {url.SafeUrl()} - the copy needs to have a different name than the parent (use @name = xxx)");
42+
progress.Error(UrlConfig, $"Error - when applying copy {UrlConfig.SafeUrl()} to {protoConfig.FullUrl} - the copy needs to have a different name than the parent (use @name = xxx)");
4443
}
4544
else
4645
{
47-
progress.ApplyingCopy(url, UrlConfig);
48-
file.AddConfig(clone);
46+
progress.ApplyingCopy(protoConfig, UrlConfig);
47+
listNode = databaseConfigs.AddAfter(listNode, new ProtoUrlConfig(protoConfig.UrlFile, clone));
4948
}
5049
}
5150
catch (Exception ex)
5251
{
53-
progress.Exception(UrlConfig, $"Exception while applying copy {UrlConfig.SafeUrl()} to {url.SafeUrl()}", ex);
52+
progress.Exception(UrlConfig, $"Exception while applying copy {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex);
5453
}
5554
}
5655
}

ModuleManager/Patches/DeletePatch.cs

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Generic;
23
using ModuleManager.Extensions;
34
using ModuleManager.Logging;
45
using ModuleManager.Patches.PassSpecifiers;
@@ -19,31 +20,29 @@ public DeletePatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSp
1920
PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier));
2021
}
2122

22-
public void Apply(UrlDir.UrlFile file, IPatchProgress progress, IBasicLogger logger)
23+
public void Apply(LinkedList<IProtoUrlConfig> databaseConfigs, IPatchProgress progress, IBasicLogger logger)
2324
{
24-
if (file == null) throw new ArgumentNullException(nameof(file));
25+
if (databaseConfigs == null) throw new ArgumentNullException(nameof(databaseConfigs));
2526
if (progress == null) throw new ArgumentNullException(nameof(progress));
2627
if (logger == null) throw new ArgumentNullException(nameof(logger));
2728

28-
int i = 0;
29-
while (i < file.configs.Count)
29+
LinkedListNode<IProtoUrlConfig> currentNode = databaseConfigs.First;
30+
while (currentNode != null)
3031
{
31-
UrlDir.UrlConfig url = file.configs[i];
32+
IProtoUrlConfig protoConfig = currentNode.Value;
3233
try
3334
{
34-
if (NodeMatcher.IsMatch(url.config))
35+
LinkedListNode<IProtoUrlConfig> nextNode = currentNode.Next;
36+
if (NodeMatcher.IsMatch(protoConfig.Node))
3537
{
36-
progress.ApplyingDelete(url, UrlConfig);
37-
file.configs.RemoveAt(i);
38-
}
39-
else
40-
{
41-
i++;
38+
progress.ApplyingDelete(protoConfig, UrlConfig);
39+
databaseConfigs.Remove(currentNode);
4240
}
41+
currentNode = nextNode;
4342
}
4443
catch (Exception ex)
4544
{
46-
progress.Exception(UrlConfig, $"Exception while applying delete {UrlConfig.SafeUrl()} to {url.SafeUrl()}", ex);
45+
progress.Exception(UrlConfig, $"Exception while applying delete {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex);
4746
}
4847
}
4948
}

0 commit comments

Comments
 (0)