├── .gitignore
├── CopyLocalFalse.txt
├── Makefile
├── ModuleManager.sln
├── ModuleManager.sln.DotSettings
├── ModuleManager.userprefs
├── ModuleManager
├── Cats
│ ├── CatAnimator.cs
│ ├── CatManager.cs
│ ├── CatMover.cs
│ └── CatOrbiter.cs
├── Collections
│ ├── ArrayEnumerator.cs
│ ├── ImmutableStack.cs
│ ├── KeyValueCache.cs
│ └── MessageQueue.cs
├── Command.cs
├── CommandParser.cs
├── CustomConfigsManager.cs
├── ExceptionIntercept
│ └── InterceptLogHandler.cs
├── Extensions
│ ├── ByteArrayExtensions.cs
│ ├── ConfigNodeExtensions.cs
│ ├── IBasicLoggerExtensions.cs
│ ├── NodeStackExtensions.cs
│ ├── StringExtensions.cs
│ ├── UrlConfigExtensions.cs
│ ├── UrlDirExtensions.cs
│ └── UrlFileExtensions.cs
├── FatalErrorHandler.cs
├── FilePathRepository.cs
├── Fix16.cs
├── Logging
│ ├── IBasicLogger.cs
│ ├── ILogMessage.cs
│ ├── LogMessage.cs
│ ├── LogSplitter.cs
│ ├── PrefixLogger.cs
│ ├── QueueLogRunner.cs
│ ├── QueueLogger.cs
│ ├── StreamLogger.cs
│ └── UnityLogger.cs
├── MMPatchLoader.cs
├── MMPatchRunner.cs
├── ModListGenerator.cs
├── ModuleManager.cs
├── ModuleManager.csproj
├── ModuleManagerTestRunner.cs
├── NeedsChecker.cs
├── NodeMatcher.cs
├── Operator.cs
├── OperatorParser.cs
├── Pass.cs
├── PatchApplier.cs
├── PatchContext.cs
├── PatchExtractor.cs
├── PatchList.cs
├── Patches
│ ├── CopyPatch.cs
│ ├── DeletePatch.cs
│ ├── EditPatch.cs
│ ├── IPatch.cs
│ ├── InsertPatch.cs
│ ├── PassSpecifiers
│ │ ├── AfterPassSpecifier.cs
│ │ ├── BeforePassSpecifier.cs
│ │ ├── FinalPassSpecifier.cs
│ │ ├── FirstPassSpecifier.cs
│ │ ├── ForPassSpecifier.cs
│ │ ├── IPassSpecifier.cs
│ │ ├── InsertPassSpecifier.cs
│ │ ├── LastPassSpecifier.cs
│ │ └── LegacyPassSpecifier.cs
│ ├── PatchCompiler.cs
│ ├── ProtoPatch.cs
│ └── ProtoPatchBuilder.cs
├── PostPatchLoader.cs
├── Progress
│ ├── IPatchProgress.cs
│ ├── PatchProgress.cs
│ └── ProgressCounter.cs
├── Properties
│ ├── AssemblyInfo.cs
│ ├── Resources.Designer.cs
│ ├── Resources.resx
│ ├── cat-1.png
│ ├── cat-10.png
│ ├── cat-11.png
│ ├── cat-12.png
│ ├── cat-2.png
│ ├── cat-3.png
│ ├── cat-4.png
│ ├── cat-5.png
│ ├── cat-6.png
│ ├── cat-7.png
│ ├── cat-8.png
│ ├── cat-9.png
│ └── rainbow2.png
├── ProtoUrlConfig.cs
├── Tags
│ ├── Tag.cs
│ ├── TagList.cs
│ └── TagListParser.cs
├── Threading
│ ├── BackgroundTask.cs
│ ├── ITaskStatus.cs
│ ├── TaskStatus.cs
│ └── TaskStatusWrapper.cs
├── Utils
│ ├── Counter.cs
│ └── FileUtils.cs
├── copy_build.sh
└── packages.config
├── ModuleManagerTests
├── Collections
│ ├── ArrayEnumeratorTest.cs
│ ├── ImmutableStackTest.cs
│ ├── KeyValueCacheTest.cs
│ └── MessageQueueTest.cs
├── CommandParserTest.cs
├── DummyTest.cs
├── Extensions
│ ├── ByteArrayExtensionsTest.cs
│ ├── ConfigNodeExtensionsTest.cs
│ ├── IBasicLoggerExtensionsTest.cs
│ ├── NodeStackExtensionsTest.cs
│ ├── StringExtensionsTest.cs
│ ├── UrlConfigExtensionsTest.cs
│ ├── UrlDirExtensionsTest.cs
│ └── UrlFileExtensionsTest.cs
├── InGameTestRunnerTest.cs
├── Logging
│ ├── LogMessageTest.cs
│ ├── LogSplitterTest.cs
│ ├── PrefixLoggerTest.cs
│ ├── QueueLogRunnerTest.cs
│ ├── QueueLoggerTest.cs
│ ├── StreamLoggerTest.cs
│ └── UnityLoggerTest.cs
├── LoggingAssertionHelpers.cs
├── MMPatchLoaderTest.cs
├── ModuleManagerTests.csproj
├── NeedsCheckerTest.cs
├── NodeMatcherTest.cs
├── OperatorParserTest.cs
├── PassTest.cs
├── PatchApplierTest.cs
├── PatchExtractorTest.cs
├── PatchListTest.cs
├── Patches
│ ├── CopyPatchTest.cs
│ ├── DeletePatchTest.cs
│ ├── EditPatchTest.cs
│ ├── InsertPatchTest.cs
│ ├── PassSpecifiers
│ │ ├── AfterPassSpecifierTest.cs
│ │ ├── BeforePassSpecifierTest.cs
│ │ ├── FinalPassSpecifierTest.cs
│ │ ├── FirstPassSpecifierTest.cs
│ │ ├── ForPassSpecifierTest.cs
│ │ ├── InsertPassSpecifierTest.cs
│ │ ├── LastPassSpecifierTest.cs
│ │ └── LegacyPassSpecifierTest.cs
│ ├── PatchCompilerTest.cs
│ └── ProtoPatchBuilderTest.cs
├── Progress
│ └── PatchProgressTest.cs
├── Properties
│ └── AssemblyInfo.cs
├── ProtoUrlConfigTest.cs
├── Tags
│ ├── TagListParserTest.cs
│ ├── TagListTest.cs
│ └── TagTest.cs
├── Threading
│ ├── BackgroundTaskTest.cs
│ └── TaskStatusTest.cs
├── Utils
│ └── CounterTest.cs
├── app.config
└── packages.config
├── README.md
├── TestUtils
├── Properties
│ └── AssemblyInfo.cs
├── TestConfigNode.cs
├── TestUtils.csproj
└── UrlBuilder.cs
├── TestUtilsTests
├── DummyTest.cs
├── Properties
│ └── AssemblyInfo.cs
├── TestConfigNodeTest.cs
├── TestUtilsTests.csproj
├── UrlBuilderTest.cs
└── packages.config
└── Tests
├── AltEdits.cfg
├── BadlyFormed.cfg
├── NameLessNode.cfg
├── Needs.cfg
├── NodeCopy.cfg
├── NodeCreate.cfg
├── NodeDelete.cfg
├── NodeEdit.cfg
├── NodeEditWildcard.cfg
├── NodeHas.cfg
├── NodeInsert.cfg
├── NodeReplace.cfg
├── ValueCopy.cfg
├── ValueCreate.cfg
├── ValueDelete.cfg
├── ValueEdit.cfg
├── ValueEmpty.cfg
├── ValueInsert.cfg
└── ValueReplace.cfg
/.gitignore:
--------------------------------------------------------------------------------
1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
2 | [Oo]bj/
3 |
4 | # mstest test results
5 | TestResults
6 |
7 | ## Ignore Visual Studio temporary files, build results, and
8 | ## files generated by popular Visual Studio add-ons.
9 |
10 | # User-specific files
11 | *.suo
12 | *.user*
13 | *.sln.docstates
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Rr]elease/
18 | x64/
19 | *_i.c
20 | *_p.c
21 | *.ilk
22 | *.meta
23 | *.obj
24 | *.pch
25 | *.pdb
26 | *.pgc
27 | *.pgd
28 | *.rsp
29 | *.sbr
30 | *.tlb
31 | *.tli
32 | *.tlh
33 | *.tmp
34 | *.log
35 | *.vspscc
36 | *.vssscc
37 | .builds
38 |
39 | # Visual C++ cache files
40 | ipch/
41 | *.aps
42 | *.ncb
43 | *.opensdf
44 | *.sdf
45 |
46 | # Visual Studio profiler
47 | *.psess
48 | *.vsp
49 | *.vspx
50 |
51 | # Guidance Automation Toolkit
52 | *.gpState
53 |
54 | # ReSharper is a .NET coding add-in
55 | _ReSharper*
56 |
57 | # NCrunch
58 | *.ncrunch*
59 | .*crunch*.local.xml
60 |
61 | # Installshield output folder
62 | [Ee]xpress
63 |
64 | # DocProject is a documentation generator add-in
65 | DocProject/buildhelp/
66 | DocProject/Help/*.HxT
67 | DocProject/Help/*.HxC
68 | DocProject/Help/*.hhc
69 | DocProject/Help/*.hhk
70 | DocProject/Help/*.hhp
71 | DocProject/Help/Html2
72 | DocProject/Help/html
73 |
74 | # Click-Once directory
75 | publish
76 |
77 | # Publish Web Output
78 | *.Publish.xml
79 |
80 | # NuGet Packages Directory
81 | packages
82 |
83 | # Windows Azure Build Output
84 | csx
85 | *.build.csdef
86 |
87 | # Windows Store app package directory
88 | AppPackages/
89 |
90 | # Others
91 | [Bb]in
92 | [Oo]bj
93 | sql
94 | TestResults
95 | [Tt]est[Rr]esult*
96 | *.Cache
97 | ClientBin
98 | [Ss]tyle[Cc]op.*
99 | ~$*
100 | *.dbmdl
101 | Generated_Code #added for RIA/Silverlight projects
102 |
103 | # Backup & report files from converting an old project file to a newer
104 | # Visual Studio version. Backup files are not needed, because we have git ;-)
105 | _UpgradeReport_Files/
106 | Backup*/
107 | UpgradeLog*.XML
108 |
109 | ModuleManager.csproj.user
110 |
111 | ModuleManager.*.dll
112 |
113 | .vs*
114 |
--------------------------------------------------------------------------------
/CopyLocalFalse.txt:
--------------------------------------------------------------------------------
1 | Copy Local has been set to false for all dependencies.
2 | Remark by Jan Van der Haegen: This file has been added because Nuget would installs packages to the solution instead of the project if there are only PowerShell scripts... (So yes: it's safe to remove this file!)
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for building ModuleManager
2 |
3 | KSPDIR := ${HOME}/.local/share/Steam/SteamApps/common/Kerbal\ Space\ Program
4 | MANAGED := KSP_Data/Managed/
5 |
6 | INCLUDEFILES := $(wildcard *.cs) \
7 | $(wildcard Properties/*.cs)
8 |
9 | RESGEN2 := resgen2
10 | GMCS := /usr/bin/gmcs
11 | GIT := /usr/bin/git
12 | TAR := /usr/bin/tar
13 | ZIP := /usr/bin/zip
14 |
15 | all: build
16 |
17 | info:
18 | @echo "== ModuleManager Build Information =="
19 | @echo " gmcs: ${GMCS}"
20 | @echo " git: ${GIT}"
21 | @echo " tar: ${TAR}"
22 | @echo " zip: ${ZIP}"
23 | @echo " KSP Data: ${KSPDIR}"
24 | @echo "====================================="
25 |
26 | build: info
27 | mkdir -p build
28 | ${RESGEN2} -usesourcepath Properties/Resources.resx build/Resources.resources
29 | ${GMCS} -t:library -lib:${KSPDIR}/${MANAGED} \
30 | -r:Assembly-CSharp,Assembly-CSharp-firstpass,UnityEngine \
31 | -out:build/ModuleManager.dll \
32 | -resource:build/Resources.resources,ModuleManager.Properties.Resources.resources \
33 | ${INCLUDEFILES}
34 |
35 | tar.gz: build
36 | ${TAR} zcf ModuleManager-0.$(shell ${GIT} rev-list --count HEAD).g$(shell ${GIT} log -1 --format="%h").tar.gz build/ModuleManager.dll
37 |
38 | zip: build
39 | ${ZIP} -9 -r ModuleManager-0.$(shell ${GIT} rev-list --count HEAD).g$(shell ${GIT} log -1 --format="%h").zip build/ModuleManager.dll
40 |
41 | clean:
42 | @echo "Cleaning up build and package directories..."
43 | rm -rf build/ package/
44 |
45 | install: build
46 | mkdir -p ${KSPDIR}/GameData/
47 | cp build/ModuleManager.dll ${KSPDIR}/GameData/
48 |
49 | uninstall: info
50 | rm -f ${KSPDIR}/GameData/ModuleManager.dll
51 |
52 |
53 | .PHONY : all info build tar.gz zip clean install uninstall
54 |
--------------------------------------------------------------------------------
/ModuleManager.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.26730.12
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModuleManager", "ModuleManager\ModuleManager.csproj", "{02C8E3AF-69F9-4102-AB60-DD6DE60662D3}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModuleManagerTests", "ModuleManagerTests\ModuleManagerTests.csproj", "{BC2A08C8-64EF-4823-A40B-8889C1CCFD75}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtils", "TestUtils\TestUtils.csproj", "{20EAAFE6-510D-4374-8D2F-6B52D0178E85}"
11 | EndProject
12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilsTests", "TestUtilsTests\TestUtilsTests.csproj", "{E695C11F-4217-4014-9B51-7232A654C205}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {02C8E3AF-69F9-4102-AB60-DD6DE60662D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {02C8E3AF-69F9-4102-AB60-DD6DE60662D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {02C8E3AF-69F9-4102-AB60-DD6DE60662D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {02C8E3AF-69F9-4102-AB60-DD6DE60662D3}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {BC2A08C8-64EF-4823-A40B-8889C1CCFD75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {BC2A08C8-64EF-4823-A40B-8889C1CCFD75}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {BC2A08C8-64EF-4823-A40B-8889C1CCFD75}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {BC2A08C8-64EF-4823-A40B-8889C1CCFD75}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {20EAAFE6-510D-4374-8D2F-6B52D0178E85}.Release|Any CPU.Build.0 = Release|Any CPU
32 | {E695C11F-4217-4014-9B51-7232A654C205}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33 | {E695C11F-4217-4014-9B51-7232A654C205}.Debug|Any CPU.Build.0 = Debug|Any CPU
34 | {E695C11F-4217-4014-9B51-7232A654C205}.Release|Any CPU.ActiveCfg = Release|Any CPU
35 | {E695C11F-4217-4014-9B51-7232A654C205}.Release|Any CPU.Build.0 = Release|Any CPU
36 | EndGlobalSection
37 | GlobalSection(SolutionProperties) = preSolution
38 | HideSolutionNode = FALSE
39 | EndGlobalSection
40 | GlobalSection(ExtensibilityGlobals) = postSolution
41 | SolutionGuid = {4DCC10BA-6036-4DCF-A78B-086DF4487922}
42 | EndGlobalSection
43 | GlobalSection(MonoDevelopProperties) = preSolution
44 | StartupItem = ModuleManager.csproj
45 | outputpath = ..\..\Plugins
46 | EndGlobalSection
47 | EndGlobal
48 |
--------------------------------------------------------------------------------
/ModuleManager.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | True
3 | DO_NOT_SHOW
4 | DO_NOT_SHOW
5 | DO_NOT_SHOW
6 | DO_NOT_SHOW
7 | DO_NOT_SHOW
8 | DO_NOT_SHOW
9 | DO_NOT_SHOW
10 | DO_NOT_SHOW
11 | DO_NOT_SHOW
12 | FAR
13 | ISP
14 | KSP
15 | ME
16 | SFS
17 | UI
18 | <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy>
19 | <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy>
20 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy>
21 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy>
22 | GUI
23 | ID
24 | UV
25 | XZU
26 | KL
27 | SRB
28 | TS
29 | SPH
30 | VAB
31 |
--------------------------------------------------------------------------------
/ModuleManager.userprefs:
--------------------------------------------------------------------------------
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 |
--------------------------------------------------------------------------------
/ModuleManager/Cats/CatAnimator.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Diagnostics.CodeAnalysis;
3 | using UnityEngine;
4 |
5 | namespace ModuleManager.Cats
6 | {
7 | class CatAnimator : MonoBehaviour
8 | {
9 |
10 | public Sprite[] frames;
11 | public float secFrame = 0.07f;
12 |
13 | private SpriteRenderer spriteRenderer;
14 | private int spriteIdx;
15 |
16 | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
17 | void Start()
18 | {
19 | spriteRenderer = GetComponent();
20 | spriteRenderer.sortingOrder = 3;
21 | StartCoroutine(Animate());
22 | }
23 |
24 |
25 | IEnumerator Animate()
26 | {
27 | if (frames.Length == 0)
28 | yield return null;
29 |
30 | WaitForSeconds yield = new WaitForSeconds(secFrame);
31 |
32 | while (true)
33 | {
34 | spriteIdx = (spriteIdx + 1) % frames.Length;
35 | spriteRenderer.sprite = frames[spriteIdx];
36 | yield return yield;
37 | }
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/ModuleManager/Cats/CatMover.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using UnityEngine;
3 |
4 | namespace ModuleManager.Cats
5 | {
6 | public class CatMover : MonoBehaviour
7 | {
8 | public Vector3 spos;
9 |
10 | public float vel = 5;
11 | private float offsetY;
12 |
13 | public TrailRenderer trail;
14 | private SpriteRenderer spriteRenderer;
15 |
16 | private int totalLenth = 100;
17 | private float activePos = 0;
18 |
19 | public float scale = 2;
20 |
21 | private const float time = 5;
22 | private const float trailTime = time / 4;
23 |
24 | private bool clearTrail = false;
25 |
26 | // Use this for initialization
27 | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
28 | void Start()
29 | {
30 | trail = GetComponent();
31 | trail.sortingOrder = 2;
32 |
33 | spriteRenderer = GetComponent();
34 |
35 | offsetY = Mathf.FloorToInt(0.2f * Screen.height);
36 |
37 | spos.z = -1;
38 |
39 | totalLenth = (int) (Screen.width / time * trail.time) + 150;
40 | trail.time = trailTime;
41 | trail.widthCurve = new AnimationCurve(new Keyframe(0, trail.startWidth ), new Keyframe(0.7f, trail.startWidth), new Keyframe(1, trail.startWidth * 0.9f));
42 | clearTrail = true;
43 | }
44 |
45 | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
46 | void Update()
47 | {
48 | if (trail.time <= 0f)
49 | {
50 | trail.time = trailTime;
51 | }
52 |
53 | activePos += ((Screen.width / time) * Time.deltaTime);
54 |
55 | if (activePos > (Screen.width + totalLenth))
56 | {
57 | activePos = -spriteRenderer.sprite.rect.width;
58 | clearTrail = true;
59 | }
60 |
61 | float f = 2f * Mathf.PI * (activePos) / (Screen.width * 0.5f);
62 |
63 | float heightOffset = Mathf.Sin(f) * (spriteRenderer.sprite.rect.height * scale);
64 |
65 | spos.x = activePos;
66 | spos.y = offsetY + heightOffset;
67 |
68 | transform.position = KSP.UI.UIMainCamera.Camera.ScreenToWorldPoint(spos);
69 | transform.rotation = Quaternion.Euler(0, 0, Mathf.Cos(f) * 0.25f * Mathf.PI * Mathf.Rad2Deg);
70 |
71 | if (clearTrail)
72 | {
73 | trail.Clear();
74 | clearTrail = false;
75 | }
76 |
77 | }
78 |
79 |
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/ModuleManager/Collections/ArrayEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace ModuleManager.Collections
6 | {
7 | public struct ArrayEnumerator : IEnumerator
8 | {
9 | private readonly T[] array;
10 | private readonly int startIndex;
11 | private readonly int length;
12 |
13 | private int index;
14 |
15 | public ArrayEnumerator(params T[] array) : this(array, 0) { }
16 |
17 | public ArrayEnumerator(T[] array, int startIndex) : this(array, startIndex, (array?.Length ?? -1) - startIndex) { }
18 |
19 | public ArrayEnumerator(T[] array, int startIndex, int length)
20 | {
21 | this.array = array ?? throw new ArgumentNullException(nameof(array));
22 |
23 | if (startIndex < 0)
24 | throw new ArgumentException($"must be non-negative (got {startIndex})", nameof(startIndex));
25 | if (startIndex > array.Length)
26 | throw new ArgumentException(
27 | $"must be less than or equal to array length (array length {array.Length}, startIndex {startIndex})",
28 | nameof(startIndex)
29 | );
30 | if (length < 0)
31 | throw new ArgumentException($"must be non-negative (got {length})", nameof(length));
32 | if (startIndex + length > array.Length)
33 | throw new ArgumentException(
34 | $"must fit within the string (array length {array.Length}, startIndex {startIndex}, length {length})",
35 | nameof(length)
36 | );
37 |
38 | this.startIndex = startIndex;
39 | this.length = length;
40 | index = startIndex - 1;
41 | }
42 |
43 | public T Current => array[index];
44 | object IEnumerator.Current => Current;
45 |
46 | public void Dispose() { }
47 |
48 | public bool MoveNext()
49 | {
50 | index++;
51 | return index < startIndex + length;
52 | }
53 |
54 | public void Reset()
55 | {
56 | index = startIndex - 1;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ModuleManager/Collections/ImmutableStack.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace ModuleManager.Collections
6 | {
7 | public class ImmutableStack : IEnumerable
8 | {
9 | public struct Enumerator : IEnumerator
10 | {
11 | private readonly ImmutableStack head;
12 | private ImmutableStack currentStack;
13 |
14 | public Enumerator(ImmutableStack stack)
15 | {
16 | head = stack;
17 | currentStack = null;
18 | }
19 |
20 | public T Current => currentStack.value;
21 | object IEnumerator.Current => Current;
22 |
23 | public void Dispose() { }
24 |
25 | public bool MoveNext()
26 | {
27 | if (currentStack == null)
28 | {
29 | currentStack = head;
30 | return true;
31 | }
32 | else if (!currentStack.IsRoot)
33 | {
34 | currentStack = currentStack.parent;
35 | return true;
36 | }
37 | else
38 | {
39 | return false;
40 | }
41 | }
42 |
43 | public void Reset() => currentStack = null;
44 | }
45 |
46 | public readonly T value;
47 | public readonly ImmutableStack parent;
48 |
49 | public ImmutableStack(T value)
50 | {
51 | this.value = value;
52 | }
53 |
54 | private ImmutableStack(T value, ImmutableStack parent)
55 | {
56 | this.value = value;
57 | this.parent = parent;
58 | }
59 |
60 | public bool IsRoot => parent == null;
61 | public ImmutableStack Root => IsRoot? this : parent.Root;
62 |
63 | public int Depth => IsRoot ? 1 : parent.Depth + 1;
64 |
65 | public ImmutableStack Push(T newValue)
66 | {
67 | return new ImmutableStack(newValue, this);
68 | }
69 |
70 | public ImmutableStack Pop()
71 | {
72 | if (IsRoot) throw new InvalidOperationException("Cannot pop from the root of a stack");
73 | return parent;
74 | }
75 |
76 | public ImmutableStack ReplaceValue(T newValue) => new ImmutableStack(newValue, parent);
77 |
78 | public Enumerator GetEnumerator() => new Enumerator(this);
79 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
80 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/ModuleManager/Collections/KeyValueCache.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace ModuleManager.Collections
5 | {
6 | public class KeyValueCache
7 | {
8 | private readonly Dictionary dict = new Dictionary();
9 | private readonly object lockObject = new object();
10 |
11 | public TValue Fetch(TKey key, Func createValue)
12 | {
13 | if (createValue == null) throw new ArgumentNullException(nameof(createValue));
14 | lock(lockObject)
15 | {
16 | if (dict.TryGetValue(key, out TValue value))
17 | {
18 | return value;
19 | }
20 | else
21 | {
22 | TValue newValue = createValue();
23 | dict.Add(key, newValue);
24 | return newValue;
25 | }
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ModuleManager/Collections/MessageQueue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 |
5 | namespace ModuleManager.Collections
6 | {
7 | public interface IMessageQueue : IEnumerable
8 | {
9 | void Add(T value);
10 | IMessageQueue TakeAll();
11 | }
12 |
13 | public class MessageQueue : IMessageQueue, IEnumerable
14 | {
15 | public sealed class Enumerator : IEnumerator
16 | {
17 | private readonly MessageQueue queue;
18 | private Node current;
19 |
20 | public Enumerator(MessageQueue queue)
21 | {
22 | this.queue = queue;
23 | }
24 |
25 | public T Current => current.value;
26 | object IEnumerator.Current => Current;
27 |
28 | public void Dispose() { }
29 |
30 | public bool MoveNext()
31 | {
32 | if (current == null)
33 | current = queue.head;
34 | else
35 | current = current.next;
36 |
37 | return current != null;
38 | }
39 |
40 | public void Reset()
41 | {
42 | current = null;
43 | }
44 | }
45 |
46 | private class Node
47 | {
48 | public Node next;
49 | public readonly T value;
50 |
51 | public Node(T value)
52 | {
53 | this.value = value;
54 | }
55 | }
56 |
57 | private readonly object lockObject = new object();
58 | private Node head;
59 | private Node tail;
60 |
61 | public void Add(T value)
62 | {
63 | Node node = new Node(value);
64 | lock (lockObject)
65 | {
66 | if (head == null)
67 | {
68 | head = node;
69 | tail = node;
70 | }
71 | else
72 | {
73 | tail.next = node;
74 | tail = node;
75 | }
76 | }
77 | }
78 |
79 | public IMessageQueue TakeAll()
80 | {
81 | MessageQueue queue = new MessageQueue();
82 | lock(lockObject)
83 | {
84 | queue.head = head;
85 | queue.tail = tail;
86 | head = null;
87 | tail = null;
88 | }
89 | return queue;
90 | }
91 |
92 | public Enumerator GetEnumerator() => new Enumerator(this);
93 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
94 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/ModuleManager/Command.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager
4 | {
5 | public enum Command
6 | {
7 | Insert,
8 |
9 | Delete,
10 |
11 | Edit,
12 |
13 | Replace,
14 |
15 | Copy,
16 |
17 | Rename,
18 |
19 | Paste,
20 |
21 | Special,
22 |
23 | Create
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ModuleManager/CommandParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager
4 | {
5 | public static class CommandParser
6 | {
7 | public static Command Parse(string name, out string valueName)
8 | {
9 | if (name.Length == 0)
10 | {
11 | valueName = string.Empty;
12 | return Command.Insert;
13 | }
14 | Command ret;
15 | switch (name[0])
16 | {
17 | case '@':
18 | ret = Command.Edit;
19 | break;
20 |
21 | case '%':
22 | ret = Command.Replace;
23 | break;
24 |
25 | case '-':
26 | case '!':
27 | ret = Command.Delete;
28 | break;
29 |
30 | case '+':
31 | case '$':
32 | ret = Command.Copy;
33 | break;
34 |
35 | case '|':
36 | ret = Command.Rename;
37 | break;
38 |
39 | case '#':
40 | ret = Command.Paste;
41 | break;
42 |
43 | case '*':
44 | ret = Command.Special;
45 | break;
46 |
47 | case '&':
48 | ret = Command.Create;
49 | break;
50 |
51 | default:
52 | valueName = name;
53 | return Command.Insert;
54 | }
55 | valueName = name.Substring(1);
56 | return ret;
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ModuleManager/CustomConfigsManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using UnityEngine;
4 |
5 | using static ModuleManager.FilePathRepository;
6 |
7 | namespace ModuleManager
8 | {
9 | [KSPAddon(KSPAddon.Startup.SpaceCentre, false)]
10 | public class CustomConfigsManager : MonoBehaviour
11 | {
12 | internal void Start()
13 | {
14 | if (HighLogic.CurrentGame.Parameters.Career.TechTreeUrl != techTreeFile && File.Exists(techTreePath))
15 | {
16 | Log("Setting modded tech tree as the active one");
17 | HighLogic.CurrentGame.Parameters.Career.TechTreeUrl = techTreeFile;
18 | }
19 | }
20 |
21 | public static void Log(String s)
22 | {
23 | print("[CustomConfigsManager] " + s);
24 | }
25 |
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ModuleManager/ExceptionIntercept/InterceptLogHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Reflection;
6 | using UnityEngine;
7 | using Object = UnityEngine.Object;
8 |
9 | namespace ModuleManager.UnityLogHandle
10 | {
11 | class InterceptLogHandler : ILogHandler
12 | {
13 | private readonly ILogHandler baseLogHandler;
14 | private readonly List brokenAssemblies = new List();
15 | private readonly int gamePathLength;
16 |
17 | public static string Warnings { get; private set; } = "";
18 |
19 | public InterceptLogHandler(ILogHandler baseLogHandler)
20 | {
21 | this.baseLogHandler = baseLogHandler ?? throw new ArgumentNullException(nameof(baseLogHandler));
22 | gamePathLength = Path.GetFullPath(KSPUtil.ApplicationRootPath).Length;
23 | }
24 |
25 | public void LogFormat(LogType logType, Object context, string format, params object[] args)
26 | {
27 | baseLogHandler.LogFormat(logType, context, format, args);
28 | }
29 |
30 | public void LogException(Exception exception, Object context)
31 | {
32 | baseLogHandler.LogException(exception, context);
33 |
34 | if (exception is ReflectionTypeLoadException ex)
35 | {
36 | string message = "Intercepted a ReflectionTypeLoadException. List of broken DLLs:\n";
37 | try
38 | {
39 | var assemblies = ex.Types.Where(x => x != null).Select(x => x.Assembly).Distinct();
40 | foreach (Assembly assembly in assemblies)
41 | {
42 | if (Warnings == "")
43 | {
44 | Warnings = "Mod(s) DLL that are not compatible with this version of KSP\n";
45 | }
46 | string modInfo = assembly.GetName().Name + " " + assembly.GetName().Version + " " +
47 | assembly.Location.Remove(0, gamePathLength) + "\n";
48 | if (!brokenAssemblies.Contains(assembly))
49 | {
50 | brokenAssemblies.Add(assembly);
51 | Warnings += modInfo;
52 | }
53 | message += modInfo;
54 | }
55 | }
56 | catch (Exception e)
57 | {
58 | message += "Exception " + e.GetType().Name + " while handling the exception...";
59 | }
60 | ModuleManager.Log(message);
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/ModuleManager/Extensions/ByteArrayExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Extensions
4 | {
5 | public static class ByteArrayExtensions
6 | {
7 | public static string ToHex(this byte[] data)
8 | {
9 | if (data == null) throw new ArgumentNullException(nameof(data));
10 | char[] result = new char[data.Length * 2];
11 |
12 | for (int i = 0; i < data.Length; i++)
13 | {
14 | result[i * 2] = GetHexValue(data[i] / 16);
15 | result[i * 2 + 1] = GetHexValue(data[i] % 16);
16 | }
17 |
18 | return new string(result);
19 | }
20 |
21 | private static char GetHexValue(int i)
22 | {
23 | if (i < 10)
24 | return (char)(i + '0');
25 | else
26 | return (char)(i - 10 + 'a');
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ModuleManager/Extensions/ConfigNodeExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 |
4 | namespace ModuleManager.Extensions
5 | {
6 | public static class ConfigNodeExtensions
7 | {
8 | public static void ShallowCopyFrom(this ConfigNode toNode, ConfigNode fromeNode)
9 | {
10 | toNode.ClearData();
11 | foreach (ConfigNode.Value value in fromeNode.values)
12 | toNode.values.Add(value);
13 | foreach (ConfigNode node in fromeNode.nodes)
14 | toNode.nodes.Add(node);
15 | }
16 |
17 | // KSP implementation of ConfigNode.CreateCopy breaks with badly formed nodes (nodes with a blank name)
18 | public static ConfigNode DeepCopy(this ConfigNode from)
19 | {
20 | ConfigNode to = new ConfigNode(from.name);
21 | foreach (ConfigNode.Value value in from.values)
22 | to.AddValueSafe(value.name, value.value);
23 | foreach (ConfigNode node in from.nodes)
24 | {
25 | ConfigNode newNode = DeepCopy(node);
26 | to.nodes.Add(newNode);
27 | }
28 | return to;
29 | }
30 |
31 | public static void PrettyPrint(this ConfigNode node, ref StringBuilder sb, string indent)
32 | {
33 | if (sb == null) throw new ArgumentNullException(nameof(sb));
34 | if (indent == null) indent = string.Empty;
35 | if (node == null)
36 | {
37 | sb.Append(indent + "\n");
38 | return;
39 | }
40 | sb.AppendFormat("{0}{1}\n{2}{{\n", indent, node.name ?? "", indent);
41 | string newindent = indent + " ";
42 | if (node.values == null)
43 | {
44 | sb.AppendFormat("{0}\n", newindent);
45 | }
46 | else
47 | {
48 | foreach (ConfigNode.Value value in node.values)
49 | {
50 | if (value == null)
51 | sb.AppendFormat("{0}\n", newindent);
52 | else
53 | sb.AppendFormat("{0}{1} = {2}\n", newindent, value.name ?? "", value.value ?? "");
54 | }
55 | }
56 |
57 | if (node.nodes == null)
58 | {
59 | sb.AppendFormat("{0}\n", newindent);
60 | }
61 | else
62 | {
63 | foreach (ConfigNode subnode in node.nodes)
64 | {
65 | subnode.PrettyPrint(ref sb, newindent);
66 | }
67 | }
68 |
69 | sb.AppendFormat("{0}}}\n", indent);
70 | }
71 |
72 | public static void AddValueSafe(this ConfigNode node, string name, string value)
73 | {
74 | node.values.Add(new ConfigNode.Value(name, value));
75 | }
76 |
77 | public static void EscapeValuesRecursive(this ConfigNode theNode)
78 | {
79 | foreach (ConfigNode subNode in theNode.nodes)
80 | {
81 | subNode.EscapeValuesRecursive();
82 | }
83 |
84 | foreach (ConfigNode.Value value in theNode.values)
85 | {
86 | value.value = value.value.Replace("\n", "\\n");
87 | value.value = value.value.Replace("\r", "\\r");
88 | value.value = value.value.Replace("\t", "\\t");
89 | }
90 | }
91 |
92 | public static void UnescapeValuesRecursive(this ConfigNode theNode)
93 | {
94 | foreach (ConfigNode subNode in theNode.nodes)
95 | {
96 | subNode.UnescapeValuesRecursive();
97 | }
98 |
99 | foreach (ConfigNode.Value value in theNode.values)
100 | {
101 | value.value = value.value.Replace("\\n", "\n");
102 | value.value = value.value.Replace("\\r", "\r");
103 | value.value = value.value.Replace("\\t", "\t");
104 | }
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/ModuleManager/Extensions/IBasicLoggerExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 | using ModuleManager.Logging;
4 |
5 | namespace ModuleManager.Extensions
6 | {
7 | public static class IBasicLoggerExtensions
8 | {
9 | public static void Info(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Log, message));
10 | public static void Warning(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Warning, message));
11 | public static void Error(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Error, message));
12 |
13 | public static void Exception(this IBasicLogger logger, Exception exception)
14 | {
15 | if (exception == null) throw new ArgumentNullException(nameof(exception));
16 | logger.Log(new LogMessage(LogType.Exception, exception.ToString()));
17 | }
18 |
19 | public static void Exception(this IBasicLogger logger, string message, Exception exception)
20 | {
21 | if (message == null) throw new ArgumentNullException(nameof(message));
22 | if (exception == null) throw new ArgumentNullException(nameof(exception));
23 | logger.Log(new LogMessage(LogType.Exception, message + ": " + exception.ToString()));
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ModuleManager/Extensions/NodeStackExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Text;
4 | using NodeStack = ModuleManager.Collections.ImmutableStack;
5 |
6 | namespace ModuleManager.Extensions
7 | {
8 | public static class NodeStackExtensions
9 | {
10 | public static string GetPath(this NodeStack stack)
11 | {
12 | int length = stack.Sum(node => node.name.Length) + stack.Depth - 1;
13 | StringBuilder sb = new StringBuilder(length);
14 |
15 | foreach (ConfigNode node in stack)
16 | {
17 | string nodeName = node.name;
18 | sb.Insert(0, node.name);
19 | if (sb.Length < sb.Capacity) sb.Insert(0, '/');
20 | }
21 |
22 | return sb.ToString();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/ModuleManager/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace ModuleManager.Extensions
5 | {
6 | public static class StringExtensions
7 | {
8 | public static bool IsBracketBalanced(this string s)
9 | {
10 | int level = 0;
11 | foreach (char c in s)
12 | {
13 | if (c == '[') level++;
14 | else if (c == ']') level--;
15 |
16 | if (level < 0) return false;
17 | }
18 | return level == 0;
19 | }
20 |
21 | private static readonly Regex whitespaceRegex = new Regex(@"\s+");
22 |
23 | public static string RemoveWS(this string withWhite)
24 | {
25 | return whitespaceRegex.Replace(withWhite, "");
26 | }
27 |
28 | public static bool Contains(this string str, string value, out int index)
29 | {
30 | if (str == null) throw new ArgumentNullException(nameof(str));
31 | if (value == null) throw new ArgumentNullException(nameof(value));
32 |
33 | index = str.IndexOf(value, StringComparison.CurrentCultureIgnoreCase);
34 | return index != -1;
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/ModuleManager/Extensions/UrlConfigExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 |
4 | namespace ModuleManager.Extensions
5 | {
6 | public static class UrlConfigExtensions
7 | {
8 | public static string SafeUrl(this UrlDir.UrlConfig url)
9 | {
10 | if (url == null) return "";
11 |
12 | string nodeName;
13 |
14 | if (!string.IsNullOrEmpty(url.type?.Trim()))
15 | {
16 | nodeName = url.type;
17 | }
18 | else if (url.type == null)
19 | {
20 | nodeName = "";
21 | }
22 | else
23 | {
24 | nodeName = "";
25 | }
26 |
27 | string parentUrl = null;
28 |
29 | if (url.parent != null)
30 | {
31 | try
32 | {
33 | parentUrl = url.parent.url;
34 | }
35 | catch
36 | {
37 | parentUrl = "";
38 | }
39 | }
40 |
41 | if (parentUrl == null)
42 | return nodeName;
43 | else
44 | return parentUrl + "/" + nodeName;
45 | }
46 |
47 | public static string PrettyPrint(this UrlDir.UrlConfig config)
48 | {
49 | if (config == null) return "";
50 |
51 | StringBuilder sb = new StringBuilder();
52 |
53 | sb.Append(config.SafeUrl());
54 | sb.Append('\n');
55 | config.config.PrettyPrint(ref sb, " ");
56 |
57 | return sb.ToString();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/ModuleManager/Extensions/UrlDirExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | namespace ModuleManager.Extensions
5 | {
6 | public static class UrlDirExtensions
7 | {
8 | public static UrlDir.UrlFile Find(this UrlDir urlDir, string url)
9 | {
10 | if (urlDir == null) throw new ArgumentNullException(nameof(urlDir));
11 | if (url == null) throw new ArgumentNullException(nameof(url));
12 | string[] splits = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
13 |
14 | UrlDir currentDir = urlDir;
15 |
16 | for (int i = 0; i < splits.Length - 1; i++)
17 | {
18 | currentDir = currentDir.children.FirstOrDefault(subDir => subDir.name == splits[i]);
19 | if (currentDir == null) return null;
20 | }
21 |
22 | string fileName = splits[splits.Length - 1];
23 | string fileExtension = null;
24 |
25 | int idx = fileName.LastIndexOf('.');
26 |
27 | if (idx > -1)
28 | {
29 | fileExtension = fileName.Substring(idx + 1);
30 | fileName = fileName.Substring(0, idx);
31 | }
32 |
33 | foreach (UrlDir.UrlFile file in currentDir.files)
34 | {
35 | if (file.name != fileName) continue;
36 | if (fileExtension != null && fileExtension != file.fileExtension) continue;
37 | return file;
38 | }
39 |
40 | return null;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/ModuleManager/Extensions/UrlFileExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Extensions
4 | {
5 | public static class UrlFileExtensions
6 | {
7 | public static string GetUrlWithExtension(this UrlDir.UrlFile urlFile)
8 | {
9 | return $"{urlFile.url}.{urlFile.fileExtension}";
10 | }
11 | public static string GetNameWithExtension(this UrlDir.UrlFile urlFile)
12 | {
13 | return $"{urlFile.name}.{urlFile.fileExtension}";
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ModuleManager/FatalErrorHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace ModuleManager
5 | {
6 | public static class FatalErrorHandler
7 | {
8 | public static void HandleFatalError(string message)
9 | {
10 | try
11 | {
12 | PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f),
13 | new Vector2(0.5f, 0.5f),
14 | new MultiOptionDialog(
15 | "ModuleManagerFatalError",
16 | $"ModuleManager has encountered a fatal error and KSP needs to close.\n\n{message}\n\nPlease see KSP's log for addtional details",
17 | "ModuleManager - Fatal Error",
18 | HighLogic.UISkin,
19 | new Rect(0.5f, 0.5f, 500f, 60f),
20 | new DialogGUIFlexibleSpace(),
21 | new DialogGUIHorizontalLayout(
22 | new DialogGUIFlexibleSpace(),
23 | new DialogGUIButton("Quit", Application.Quit, 140.0f, 30.0f, true),
24 | new DialogGUIFlexibleSpace()
25 | )
26 | ),
27 | true,
28 | HighLogic.UISkin);
29 | }
30 | catch(Exception ex)
31 | {
32 | Debug.LogError("Exception while trying to create the fatal exception dialog");
33 | Debug.LogException(ex);
34 | Application.Quit();
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/ModuleManager/FilePathRepository.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace ModuleManager
5 | {
6 | internal static class FilePathRepository
7 | {
8 | internal static readonly string normalizedRootPath = Path.GetFullPath(KSPUtil.ApplicationRootPath);
9 | internal static readonly string cachePath = Path.Combine(normalizedRootPath, "GameData", "ModuleManager.ConfigCache");
10 |
11 | internal static readonly string techTreeFile = Path.Combine("GameData", "ModuleManager.TechTree");
12 | internal static readonly string techTreePath = Path.Combine(normalizedRootPath, techTreeFile);
13 |
14 | internal static readonly string physicsFile = Path.Combine("GameData", "ModuleManager.Physics");
15 | internal static readonly string physicsPath = Path.Combine(normalizedRootPath, physicsFile);
16 | internal static readonly string defaultPhysicsPath = Path.Combine(normalizedRootPath, "Physics.cfg");
17 |
18 | internal static readonly string partDatabasePath = Path.Combine(normalizedRootPath, "PartDatabase.cfg");
19 |
20 | internal static readonly string shaPath = Path.Combine(normalizedRootPath, "GameData", "ModuleManager.ConfigSHA");
21 |
22 | internal static readonly string logsDirPath = Path.Combine(normalizedRootPath, "Logs", "ModuleManager");
23 | internal static readonly string logPath = Path.Combine(logsDirPath, "ModuleManager.log");
24 | internal static readonly string patchLogPath = Path.Combine(logsDirPath, "MMPatch.log");
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ModuleManager/Fix16.cs:
--------------------------------------------------------------------------------
1 | using System.Collections;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace ModuleManager
5 | {
6 | class Fix16 : LoadingSystem
7 | {
8 | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
9 | private void Awake()
10 | {
11 | if (Instance != null)
12 | {
13 | DestroyImmediate(this);
14 | return;
15 | }
16 |
17 | Instance = this;
18 | DontDestroyOnLoad(gameObject);
19 | }
20 |
21 | private static Fix16 Instance { get; set; }
22 |
23 | private bool ready;
24 |
25 | private int count;
26 |
27 | private int current;
28 |
29 | private const int yieldStep = 20;
30 |
31 | public override bool IsReady()
32 | {
33 | return ready;
34 | }
35 |
36 | public override string ProgressTitle()
37 | {
38 | return "Fix 1.6.0 " + current + "/" + count;
39 | }
40 |
41 | public override float ProgressFraction()
42 | {
43 | return (float) current / count;
44 | }
45 |
46 | public override void StartLoad()
47 | {
48 | ready = false;
49 |
50 | count = PartLoader.LoadedPartsList.Count;
51 |
52 | StartCoroutine(DoFix());
53 | }
54 |
55 | private IEnumerator DoFix()
56 | {
57 | int yieldCounter = 0;
58 | for (current = 0; current < count; current++)
59 | {
60 | AvailablePart avp = PartLoader.LoadedPartsList[current];
61 | if (avp.partPrefab.dragModel == Part.DragModel.CUBE && !avp.partPrefab.DragCubes.Procedural &&
62 | !avp.partPrefab.DragCubes.None && avp.partPrefab.DragCubes.Cubes.Count == 0)
63 | {
64 | DragCubeSystem.Instance.LoadDragCubes(avp.partPrefab);
65 | }
66 |
67 | if (yieldCounter++ >= yieldStep)
68 | {
69 | yieldCounter = 0;
70 | yield return null;
71 | }
72 | }
73 |
74 | ready = true;
75 | yield return null;
76 | }
77 |
78 | public override float LoadWeight()
79 | {
80 | return 0.1f;
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/IBasicLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Logging
4 | {
5 | // Stripped down version of UnityEngine.ILogger
6 | public interface IBasicLogger
7 | {
8 | void Log(ILogMessage message);
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/ILogMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace ModuleManager.Logging
5 | {
6 | public interface ILogMessage
7 | {
8 | LogType LogType { get; }
9 | DateTime Timestamp { get; }
10 | string Message { get; }
11 | string ToLogString();
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/LogMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace ModuleManager.Logging
5 | {
6 | public class LogMessage : ILogMessage
7 | {
8 | private const string DATETIME_FORMAT_STRING = "HH:mm:ss.fff";
9 |
10 | public LogType LogType { get; }
11 | public DateTime Timestamp { get; }
12 | public string Message { get; }
13 |
14 | public LogMessage(LogType logType, string message)
15 | {
16 | LogType = logType;
17 | Timestamp = DateTime.Now;
18 | Message = message ?? throw new ArgumentNullException(nameof(message));
19 | }
20 |
21 | public LogMessage(ILogMessage logMessage, string newMessage)
22 | {
23 | if (logMessage == null) throw new ArgumentNullException(nameof(logMessage));
24 | LogType = logMessage.LogType;
25 | Timestamp = logMessage.Timestamp;
26 | Message = newMessage ?? throw new ArgumentNullException(nameof(newMessage));
27 | }
28 |
29 | public string ToLogString()
30 | {
31 | string prefix;
32 | if (LogType == LogType.Log)
33 | prefix = "LOG";
34 | else if (LogType == LogType.Warning)
35 | prefix = "WRN";
36 | else if (LogType == LogType.Error)
37 | prefix = "ERR";
38 | else if (LogType == LogType.Assert)
39 | prefix = "AST";
40 | else if (LogType == LogType.Exception)
41 | prefix = "EXC";
42 | else
43 | prefix = "???";
44 |
45 | return $"[{prefix} {Timestamp.ToString(DATETIME_FORMAT_STRING)}] {Message}";
46 | }
47 |
48 | public override string ToString()
49 | {
50 | return $"[{GetType().FullName} LogType={LogType} Message={Message}]";
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/LogSplitter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Logging
4 | {
5 | public class LogSplitter : IBasicLogger
6 | {
7 | private readonly IBasicLogger logger1;
8 | private readonly IBasicLogger logger2;
9 |
10 | public LogSplitter(IBasicLogger logger1, IBasicLogger logger2)
11 | {
12 | this.logger1 = logger1 ?? throw new ArgumentNullException(nameof(logger1));
13 | this.logger2 = logger2 ?? throw new ArgumentNullException(nameof(logger2));
14 | }
15 |
16 | public void Log(ILogMessage message)
17 | {
18 | if (message == null) throw new ArgumentNullException(nameof(message));
19 | logger1.Log(message);
20 | logger2.Log(message);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/PrefixLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Logging
4 | {
5 | public class PrefixLogger : IBasicLogger
6 | {
7 | private readonly string prefix;
8 | private readonly IBasicLogger logger;
9 |
10 | public PrefixLogger(string prefix, IBasicLogger logger)
11 | {
12 | if (string.IsNullOrEmpty(prefix)) throw new ArgumentNullException(nameof(prefix));
13 | this.prefix = $"[{prefix}] ";
14 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
15 | }
16 |
17 | public void Log(ILogMessage message)
18 | {
19 | if (message == null) throw new ArgumentNullException(nameof(message));
20 | logger.Log(new LogMessage(message, prefix + message.Message));
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/QueueLogRunner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | using ModuleManager.Collections;
5 |
6 | namespace ModuleManager.Logging
7 | {
8 | public class QueueLogRunner
9 | {
10 | private enum State
11 | {
12 | Initialized,
13 | Running,
14 | StopRequested,
15 | Stopped,
16 | }
17 |
18 | private State state = State.Initialized;
19 | private readonly IMessageQueue logQueue;
20 | private readonly long timeToWaitForLogsMs;
21 |
22 | public QueueLogRunner(IMessageQueue logQueue, long timeToWaitForLogsMs = 50)
23 | {
24 | this.logQueue = logQueue ?? throw new ArgumentNullException(nameof(logQueue));
25 | if (timeToWaitForLogsMs < 0) throw new ArgumentException("must be non-negative", nameof(timeToWaitForLogsMs));
26 | this.timeToWaitForLogsMs = timeToWaitForLogsMs;
27 | }
28 |
29 | public void RequestStop()
30 | {
31 | if (state == State.StopRequested || state == State.Stopped) return;
32 | if (state != State.Running) throw new InvalidOperationException($"Cannot request stop from {state} state");
33 | state = State.StopRequested;
34 | }
35 |
36 | public void Run(IBasicLogger logger)
37 | {
38 | if (state != State.Initialized) throw new InvalidOperationException($"Cannot run from {state} state");
39 | if (logger == null) throw new ArgumentNullException(nameof(logger));
40 | state = State.Running;
41 |
42 | System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
43 |
44 | while (state == State.Running)
45 | {
46 | stopwatch.Start();
47 |
48 | foreach (ILogMessage message in logQueue.TakeAll())
49 | {
50 | logger.Log(message);
51 | }
52 |
53 | long timeRemaining = timeToWaitForLogsMs - stopwatch.ElapsedMilliseconds;
54 | if (timeRemaining > 0)
55 | System.Threading.Thread.Sleep((int)timeRemaining);
56 |
57 | stopwatch.Reset();
58 | }
59 |
60 | foreach (ILogMessage message in logQueue.TakeAll())
61 | {
62 | logger.Log(message);
63 | }
64 |
65 | state = State.Stopped;
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/QueueLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Collections;
3 |
4 | namespace ModuleManager.Logging
5 | {
6 | public class QueueLogger : IBasicLogger
7 | {
8 | private readonly IMessageQueue queue;
9 |
10 | public QueueLogger(IMessageQueue queue)
11 | {
12 | this.queue = queue ?? throw new ArgumentNullException(nameof(queue));
13 | }
14 |
15 | public void Log(ILogMessage message)
16 | {
17 | if (message == null) throw new ArgumentNullException(nameof(message));
18 | queue.Add(message);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/StreamLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace ModuleManager.Logging
5 | {
6 | public sealed class StreamLogger : IBasicLogger, IDisposable
7 | {
8 | private readonly StreamWriter streamWriter;
9 | private bool disposed = false;
10 |
11 | public StreamLogger(Stream stream)
12 | {
13 | streamWriter = new StreamWriter(stream);
14 | }
15 |
16 | public void Log(ILogMessage message)
17 | {
18 | if (disposed) throw new InvalidOperationException("Object has already been disposed");
19 | if (message == null) throw new ArgumentNullException(nameof(message));
20 |
21 | streamWriter.WriteLine(message.ToLogString());
22 | }
23 |
24 | public void Dispose()
25 | {
26 | // Flushes and closes the StreamWriter and the underlying stream
27 | streamWriter.Close();
28 |
29 | disposed = true;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ModuleManager/Logging/UnityLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using UnityEngine;
3 |
4 | namespace ModuleManager.Logging
5 | {
6 | public class UnityLogger : IBasicLogger
7 | {
8 | private readonly ILogger logger;
9 |
10 | public UnityLogger(ILogger logger)
11 | {
12 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
13 | }
14 |
15 | public void Log(ILogMessage message)
16 | {
17 | if (message == null) throw new ArgumentNullException(nameof(message));
18 | logger.Log(message.LogType, message.Message);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ModuleManager/MMPatchRunner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using ModuleManager.Collections;
6 | using ModuleManager.Extensions;
7 | using ModuleManager.Logging;
8 | using ModuleManager.Threading;
9 |
10 | using static ModuleManager.FilePathRepository;
11 |
12 | namespace ModuleManager
13 | {
14 | public class MMPatchRunner
15 | {
16 | private readonly IBasicLogger kspLogger;
17 |
18 | public string Status { get; private set; } = "";
19 | public string Errors { get; private set; } = "";
20 |
21 | public MMPatchRunner(IBasicLogger kspLogger)
22 | {
23 | this.kspLogger = kspLogger ?? throw new ArgumentNullException(nameof(kspLogger));
24 | }
25 |
26 | public IEnumerator Run()
27 | {
28 | PostPatchLoader.Instance.databaseConfigs = null;
29 |
30 | if (!Directory.Exists(logsDirPath)) Directory.CreateDirectory(logsDirPath);
31 |
32 | kspLogger.Info("Patching started on a new thread, all output will be directed to " + logPath);
33 |
34 | MessageQueue mmLogQueue = new MessageQueue();
35 | QueueLogRunner logRunner = new QueueLogRunner(mmLogQueue);
36 | ITaskStatus loggingThreadStatus = BackgroundTask.Start(delegate
37 | {
38 | using StreamLogger streamLogger = new StreamLogger(new FileStream(logPath, FileMode.Create));
39 | streamLogger.Info("Log started at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
40 | logRunner.Run(streamLogger);
41 | streamLogger.Info("Done!");
42 | });
43 |
44 | // Wait for game database to be initialized for the 2nd time and wait for any plugins to initialize
45 | yield return null;
46 | yield return null;
47 |
48 | IBasicLogger mmLogger = new QueueLogger(mmLogQueue);
49 |
50 | IEnumerable modsAddedByAssemblies = ModListGenerator.GetAdditionalModsFromStaticMethods(mmLogger);
51 |
52 | IEnumerable databaseConfigs = null;
53 |
54 | MMPatchLoader patchLoader = new MMPatchLoader(modsAddedByAssemblies, mmLogger);
55 |
56 | ITaskStatus patchingThreadStatus = BackgroundTask.Start(delegate
57 | {
58 | databaseConfigs = patchLoader.Run();
59 | });
60 |
61 | while(true)
62 | {
63 | yield return null;
64 |
65 | if (!patchingThreadStatus.IsRunning)
66 | logRunner.RequestStop();
67 |
68 | Status = patchLoader.status;
69 | Errors = patchLoader.errors;
70 |
71 | if (!patchingThreadStatus.IsRunning && !loggingThreadStatus.IsRunning) break;
72 | }
73 |
74 | if (patchingThreadStatus.IsExitedWithError)
75 | {
76 | kspLogger.Exception("The patching thread threw an exception", patchingThreadStatus.Exception);
77 | FatalErrorHandler.HandleFatalError("The patching thread threw an exception");
78 | yield break;
79 | }
80 |
81 | if (loggingThreadStatus.IsExitedWithError)
82 | {
83 | kspLogger.Exception("The logging thread threw an exception", loggingThreadStatus.Exception);
84 | FatalErrorHandler.HandleFatalError("The logging thread threw an exception");
85 | yield break;
86 | }
87 |
88 | if (databaseConfigs == null)
89 | {
90 | kspLogger.Error("The patcher returned a null collection of configs");
91 | FatalErrorHandler.HandleFatalError("The patcher returned a null collection of configs");
92 | yield break;
93 | }
94 |
95 | PostPatchLoader.Instance.databaseConfigs = databaseConfigs;
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/ModuleManager/ModuleManagerTestRunner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using ModuleManager.Extensions;
6 | using ModuleManager.Logging;
7 |
8 | namespace ModuleManager
9 | {
10 | public class InGameTestRunner
11 | {
12 | private readonly IBasicLogger logger;
13 |
14 | public InGameTestRunner(IBasicLogger logger)
15 | {
16 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
17 | }
18 |
19 | public void RunTestCases(UrlDir gameDatabaseRoot)
20 | {
21 | if (gameDatabaseRoot == null) throw new ArgumentNullException(nameof(gameDatabaseRoot));
22 | logger.Info("Running tests...");
23 |
24 | foreach (UrlDir.UrlConfig expect in gameDatabaseRoot.GetConfigs("MMTEST_EXPECT"))
25 | {
26 | // So for each of the expects, we expect all the configs before that node to match exactly.
27 | UrlDir.UrlFile parent = expect.parent;
28 | if (parent.configs.Count != expect.config.CountNodes + 1)
29 | {
30 | logger.Error("Test " + parent.name + " failed as expected number of nodes differs expected: " +
31 | expect.config.CountNodes + " found: " + (parent.configs.Count - 1));
32 | for (int i = 0; i < parent.configs.Count; ++i)
33 | logger.Info(parent.configs[i].config.ToString());
34 | continue;
35 | }
36 | for (int i = 0; i < expect.config.CountNodes; ++i)
37 | {
38 | ConfigNode gotNode = parent.configs[i].config;
39 | ConfigNode expectNode = expect.config.nodes[i];
40 | if (!CompareRecursive(expectNode, gotNode))
41 | {
42 | logger.Error("Test " + parent.name + "[" + i +
43 | "] failed as expected output and actual output differ.\nexpected:\n" + expectNode +
44 | "\nActually got:\n" + gotNode);
45 | }
46 | }
47 |
48 | // Purge the tests
49 | parent.configs.Clear();
50 | }
51 | logger.Info("tests complete.");
52 | }
53 |
54 | private static bool CompareRecursive(ConfigNode expectNode, ConfigNode gotNode)
55 | {
56 | if (expectNode.values.Count != gotNode.values.Count || expectNode.nodes.Count != gotNode.nodes.Count)
57 | return false;
58 | for (int i = 0; i < expectNode.values.Count; ++i)
59 | {
60 | ConfigNode.Value eVal = expectNode.values[i];
61 | ConfigNode.Value gVal = gotNode.values[i];
62 | if (eVal.name != gVal.name || eVal.value != gVal.value)
63 | return false;
64 | }
65 | for (int i = 0; i < expectNode.nodes.Count; ++i)
66 | {
67 | ConfigNode eNode = expectNode.nodes[i];
68 | ConfigNode gNode = gotNode.nodes[i];
69 | if (!CompareRecursive(eNode, gNode))
70 | return false;
71 | }
72 | return true;
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/ModuleManager/NodeMatcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Extensions;
3 |
4 | namespace ModuleManager
5 | {
6 | public interface INodeMatcher
7 | {
8 | bool IsMatch(ConfigNode node);
9 | }
10 |
11 | public class NodeMatcher : INodeMatcher
12 | {
13 | private readonly string type;
14 | private readonly string[] namePatterns = null;
15 | private readonly string constraints = "";
16 |
17 | public NodeMatcher(string type, string name, string constraints)
18 | {
19 | if (type == string.Empty) throw new ArgumentException("can't be empty", nameof(type));
20 | this.type = type ?? throw new ArgumentNullException(nameof(type));
21 |
22 | if (name == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(name));
23 | if (constraints == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(constraints));
24 |
25 | if (name != null) namePatterns = name.Split(',', '|');
26 | if (constraints != null)
27 | {
28 | if (!constraints.IsBracketBalanced()) throw new ArgumentException("is not bracket balanced: " + constraints, nameof(constraints));
29 | this.constraints = constraints;
30 | }
31 | }
32 |
33 | public bool IsMatch(ConfigNode node)
34 | {
35 | if (node.name != type) return false;
36 |
37 | if (namePatterns != null)
38 | {
39 | string name = node.GetValue("name");
40 | if (name == null) return false;
41 |
42 | bool match = false;
43 | foreach (string pattern in namePatterns)
44 | {
45 | if (MMPatchLoader.WildcardMatch(name, pattern))
46 | {
47 | match = true;
48 | break;
49 | }
50 | }
51 |
52 | if (!match) return false;
53 | }
54 |
55 | return MMPatchLoader.CheckConstraints(node, constraints);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/ModuleManager/Operator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager
4 | {
5 | public enum Operator
6 | {
7 | Assign,
8 | Add,
9 | Subtract,
10 | Multiply,
11 | Divide,
12 | Exponentiate,
13 | RegexReplace,
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/ModuleManager/OperatorParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager
4 | {
5 | public static class OperatorParser
6 | {
7 | public static Operator Parse(string name, out string valueName)
8 | {
9 | if (name == null) throw new ArgumentNullException(nameof(name));
10 |
11 | if (name.Length == 0)
12 | {
13 | valueName = string.Empty;
14 | return Operator.Assign;
15 | }
16 | else if (name.Length == 1 || (name[name.Length - 2] != ' ' && name[name.Length - 2] != '\t'))
17 | {
18 | valueName = name;
19 | return Operator.Assign;
20 | }
21 |
22 | Operator ret;
23 | switch (name[name.Length - 1])
24 | {
25 | case '+':
26 | ret = Operator.Add;
27 | break;
28 |
29 | case '-':
30 | ret = Operator.Subtract;
31 | break;
32 |
33 | case '*':
34 | ret = Operator.Multiply;
35 | break;
36 |
37 | case '/':
38 | ret = Operator.Divide;
39 | break;
40 |
41 | case '!':
42 | ret = Operator.Exponentiate;
43 | break;
44 |
45 | case '^':
46 | ret = Operator.RegexReplace;
47 | break;
48 |
49 | default:
50 | valueName = name;
51 | return Operator.Assign;
52 | }
53 | valueName = name.Substring(0, name.Length - 1).TrimEnd();
54 | return ret;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/ModuleManager/Pass.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using ModuleManager.Patches;
5 |
6 | namespace ModuleManager
7 | {
8 | public interface IPass : IEnumerable
9 | {
10 | string Name { get; }
11 | }
12 |
13 | public class Pass : IPass
14 | {
15 | private readonly string name;
16 | private readonly List patches = new List(0);
17 |
18 | public Pass(string name)
19 | {
20 | this.name = name ?? throw new ArgumentNullException(nameof(name));
21 | if (name == string.Empty) throw new ArgumentException("can't be empty", nameof(name));
22 | }
23 |
24 | public string Name => name;
25 |
26 | public void Add(IPatch patch) => patches.Add(patch);
27 |
28 | public List.Enumerator GetEnumerator() => patches.GetEnumerator();
29 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
30 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ModuleManager/PatchApplier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using ModuleManager.Logging;
4 | using ModuleManager.Extensions;
5 | using ModuleManager.Patches;
6 | using ModuleManager.Progress;
7 |
8 | namespace ModuleManager
9 | {
10 | public class PatchApplier
11 | {
12 | private readonly IBasicLogger logger;
13 | private readonly IPatchProgress progress;
14 |
15 | public PatchApplier(IPatchProgress progress, IBasicLogger logger)
16 | {
17 | this.progress = progress ?? throw new ArgumentNullException(nameof(progress));
18 | this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
19 | }
20 |
21 | public IEnumerable ApplyPatches(IEnumerable patches)
22 | {
23 | if (patches == null) throw new ArgumentNullException(nameof(patches));
24 |
25 | LinkedList databaseConfigs = new LinkedList();
26 |
27 | foreach (IPass pass in patches)
28 | {
29 | ApplyPatches(databaseConfigs, pass);
30 | }
31 |
32 | return databaseConfigs;
33 | }
34 |
35 | private void ApplyPatches(LinkedList databaseConfigs, IPass pass)
36 | {
37 | progress.PassStarted(pass);
38 |
39 | foreach (IPatch patch in pass)
40 | {
41 | try
42 | {
43 | patch.Apply(databaseConfigs, progress, logger);
44 | if (patch.CountsAsPatch) progress.PatchApplied();
45 | }
46 | catch (Exception e)
47 | {
48 | progress.Exception(patch.UrlConfig, "Exception while processing node : " + patch.UrlConfig.SafeUrl(), e);
49 | logger.Error("Processed node was\n" + patch.UrlConfig.PrettyPrint());
50 | }
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/ModuleManager/PatchContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using ModuleManager.Logging;
4 | using ModuleManager.Progress;
5 |
6 | namespace ModuleManager
7 | {
8 | public struct PatchContext
9 | {
10 | public readonly UrlDir.UrlConfig patchUrl;
11 | public readonly IEnumerable databaseConfigs;
12 | public readonly IBasicLogger logger;
13 | public readonly IPatchProgress progress;
14 |
15 | public PatchContext(UrlDir.UrlConfig patchUrl, IEnumerable databaseConfigs, IBasicLogger logger, IPatchProgress progress)
16 | {
17 | this.patchUrl = patchUrl;
18 | this.databaseConfigs = databaseConfigs;
19 | this.logger = logger;
20 | this.progress = progress;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/CopyPatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using NodeStack = ModuleManager.Collections.ImmutableStack;
4 | using ModuleManager.Extensions;
5 | using ModuleManager.Logging;
6 | using ModuleManager.Patches.PassSpecifiers;
7 | using ModuleManager.Progress;
8 |
9 | namespace ModuleManager.Patches
10 | {
11 | public class CopyPatch : IPatch
12 | {
13 | public UrlDir.UrlConfig UrlConfig { get; }
14 | public INodeMatcher NodeMatcher { get; }
15 | public IPassSpecifier PassSpecifier { get; }
16 | public bool CountsAsPatch => true;
17 |
18 | public CopyPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpecifier passSpecifier)
19 | {
20 | UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig));
21 | NodeMatcher = nodeMatcher ?? throw new ArgumentNullException(nameof(nodeMatcher));
22 | PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier));
23 | }
24 |
25 | public void Apply(LinkedList databaseConfigs, IPatchProgress progress, IBasicLogger logger)
26 | {
27 | if (databaseConfigs == null) throw new ArgumentNullException(nameof(databaseConfigs));
28 | if (progress == null) throw new ArgumentNullException(nameof(progress));
29 | if (logger == null) throw new ArgumentNullException(nameof(logger));
30 |
31 | PatchContext context = new PatchContext(UrlConfig, databaseConfigs, logger, progress);
32 |
33 | for (LinkedListNode listNode = databaseConfigs.First; listNode != null; listNode = listNode.Next)
34 | {
35 | IProtoUrlConfig protoConfig = listNode.Value;
36 | try
37 | {
38 | if (!NodeMatcher.IsMatch(protoConfig.Node)) continue;
39 |
40 | ConfigNode clone = MMPatchLoader.ModifyNode(new NodeStack(protoConfig.Node), UrlConfig.config, context);
41 | if (protoConfig.Node.GetValue("name") is string name && name == clone.GetValue("name"))
42 | {
43 | 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)");
44 | }
45 | else
46 | {
47 | progress.ApplyingCopy(protoConfig, UrlConfig);
48 | listNode = databaseConfigs.AddAfter(listNode, new ProtoUrlConfig(protoConfig.UrlFile, clone));
49 | }
50 | }
51 | catch (Exception ex)
52 | {
53 | progress.Exception(UrlConfig, $"Exception while applying copy {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex);
54 | }
55 | }
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/DeletePatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using ModuleManager.Extensions;
4 | using ModuleManager.Logging;
5 | using ModuleManager.Patches.PassSpecifiers;
6 | using ModuleManager.Progress;
7 |
8 | namespace ModuleManager.Patches
9 | {
10 | public class DeletePatch : IPatch
11 | {
12 | public UrlDir.UrlConfig UrlConfig { get; }
13 | public INodeMatcher NodeMatcher { get; }
14 | public IPassSpecifier PassSpecifier { get; }
15 | public bool CountsAsPatch => true;
16 |
17 | public DeletePatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpecifier passSpecifier)
18 | {
19 | UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig));
20 | NodeMatcher = nodeMatcher ?? throw new ArgumentNullException(nameof(nodeMatcher));
21 | PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier));
22 | }
23 |
24 | public void Apply(LinkedList databaseConfigs, IPatchProgress progress, IBasicLogger logger)
25 | {
26 | if (databaseConfigs == null) throw new ArgumentNullException(nameof(databaseConfigs));
27 | if (progress == null) throw new ArgumentNullException(nameof(progress));
28 | if (logger == null) throw new ArgumentNullException(nameof(logger));
29 |
30 | LinkedListNode currentNode = databaseConfigs.First;
31 | while (currentNode != null)
32 | {
33 | IProtoUrlConfig protoConfig = currentNode.Value;
34 | try
35 | {
36 | LinkedListNode nextNode = currentNode.Next;
37 | if (NodeMatcher.IsMatch(protoConfig.Node))
38 | {
39 | progress.ApplyingDelete(protoConfig, UrlConfig);
40 | databaseConfigs.Remove(currentNode);
41 | }
42 | currentNode = nextNode;
43 | }
44 | catch (Exception ex)
45 | {
46 | progress.Exception(UrlConfig, $"Exception while applying delete {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex);
47 | }
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/EditPatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using NodeStack = ModuleManager.Collections.ImmutableStack;
4 | using ModuleManager.Extensions;
5 | using ModuleManager.Logging;
6 | using ModuleManager.Patches.PassSpecifiers;
7 | using ModuleManager.Progress;
8 |
9 | namespace ModuleManager.Patches
10 | {
11 | public class EditPatch : IPatch
12 | {
13 | private readonly bool loop;
14 |
15 | public UrlDir.UrlConfig UrlConfig { get; }
16 | public INodeMatcher NodeMatcher { get; }
17 | public IPassSpecifier PassSpecifier { get; }
18 | public bool CountsAsPatch => true;
19 |
20 | public EditPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher, IPassSpecifier passSpecifier)
21 | {
22 | UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig));
23 | NodeMatcher = nodeMatcher ?? throw new ArgumentNullException(nameof(nodeMatcher));
24 | PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier));
25 |
26 | loop = urlConfig.config.HasNode("MM_PATCH_LOOP");
27 | }
28 |
29 | public void Apply(LinkedList databaseConfigs, IPatchProgress progress, IBasicLogger logger)
30 | {
31 | if (databaseConfigs == null) throw new ArgumentNullException(nameof(databaseConfigs));
32 | if (progress == null) throw new ArgumentNullException(nameof(progress));
33 | if (logger == null) throw new ArgumentNullException(nameof(logger));
34 |
35 | PatchContext context = new PatchContext(UrlConfig, databaseConfigs, logger, progress);
36 | for (LinkedListNode listNode = databaseConfigs.First; listNode != null; listNode = listNode.Next)
37 | {
38 | IProtoUrlConfig protoConfig = listNode.Value;
39 | try
40 | {
41 | if (!NodeMatcher.IsMatch(protoConfig.Node)) continue;
42 | if (loop) logger.Info($"Looping on {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}");
43 |
44 | do
45 | {
46 | progress.ApplyingUpdate(protoConfig, UrlConfig);
47 | listNode.Value = protoConfig = new ProtoUrlConfig(protoConfig.UrlFile, MMPatchLoader.ModifyNode(new NodeStack(protoConfig.Node), UrlConfig.config, context));
48 | } while (loop && NodeMatcher.IsMatch(protoConfig.Node));
49 |
50 | if (loop) protoConfig.Node.RemoveNodes("MM_PATCH_LOOP");
51 | }
52 | catch (Exception ex)
53 | {
54 | progress.Exception(UrlConfig, $"Exception while applying update {UrlConfig.SafeUrl()} to {protoConfig.FullUrl}", ex);
55 | }
56 | }
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/IPatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using ModuleManager.Logging;
4 | using ModuleManager.Patches.PassSpecifiers;
5 | using ModuleManager.Progress;
6 |
7 | namespace ModuleManager.Patches
8 | {
9 | public interface IPatch
10 | {
11 | UrlDir.UrlConfig UrlConfig { get; }
12 | IPassSpecifier PassSpecifier { get; }
13 | bool CountsAsPatch { get; }
14 | void Apply(LinkedList configs, IPatchProgress progress, IBasicLogger logger);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/InsertPatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using ModuleManager.Extensions;
4 | using ModuleManager.Logging;
5 | using ModuleManager.Patches.PassSpecifiers;
6 | using ModuleManager.Progress;
7 |
8 | namespace ModuleManager.Patches
9 | {
10 | public class InsertPatch : IPatch
11 | {
12 | public UrlDir.UrlConfig UrlConfig { get; }
13 | public string NodeType { get; }
14 | public IPassSpecifier PassSpecifier { get; }
15 | public bool CountsAsPatch => false;
16 |
17 | public InsertPatch(UrlDir.UrlConfig urlConfig, string nodeType, IPassSpecifier passSpecifier)
18 | {
19 | UrlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig));
20 | NodeType = nodeType ?? throw new ArgumentNullException(nameof(nodeType));
21 | PassSpecifier = passSpecifier ?? throw new ArgumentNullException(nameof(passSpecifier));
22 | }
23 |
24 | public void Apply(LinkedList configs, IPatchProgress progress, IBasicLogger logger)
25 | {
26 | if (configs == null) throw new ArgumentNullException(nameof(configs));
27 | if (progress == null) throw new ArgumentNullException(nameof(progress));
28 | if (logger == null) throw new ArgumentNullException(nameof(logger));
29 |
30 | ConfigNode node = UrlConfig.config.DeepCopy();
31 | node.name = NodeType;
32 | configs.AddLast(new ProtoUrlConfig(UrlConfig.parent, node));
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/AfterPassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public class AfterPassSpecifier : IPassSpecifier
7 | {
8 | public readonly string mod;
9 | public readonly UrlDir.UrlConfig urlConfig;
10 |
11 | public AfterPassSpecifier(string mod, UrlDir.UrlConfig urlConfig)
12 | {
13 | if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod));
14 | this.mod = mod ?? throw new ArgumentNullException(nameof(mod));
15 | this.urlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig));
16 | }
17 |
18 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress)
19 | {
20 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker));
21 | if (progress == null) throw new ArgumentNullException(nameof(progress));
22 | bool result = needsChecker.CheckNeeds(mod);
23 | if (!result) progress.NeedsUnsatisfiedAfter(urlConfig);
24 | return result;
25 | }
26 |
27 | public string Descriptor => $":AFTER[{mod.ToUpper()}]";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/BeforePassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public class BeforePassSpecifier : IPassSpecifier
7 | {
8 | public readonly string mod;
9 | public readonly UrlDir.UrlConfig urlConfig;
10 |
11 | public BeforePassSpecifier(string mod, UrlDir.UrlConfig urlConfig)
12 | {
13 | if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod));
14 | this.mod = mod ?? throw new ArgumentNullException(nameof(mod));
15 | this.urlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig));
16 | }
17 |
18 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress)
19 | {
20 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker));
21 | if (progress == null) throw new ArgumentNullException(nameof(progress));
22 | bool result = needsChecker.CheckNeeds(mod);
23 | if (!result) progress.NeedsUnsatisfiedBefore(urlConfig);
24 | return result;
25 | }
26 |
27 | public string Descriptor => $":BEFORE[{mod.ToUpper()}]";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/FinalPassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public class FinalPassSpecifier : IPassSpecifier
7 | {
8 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress)
9 | {
10 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker));
11 | if (progress == null) throw new ArgumentNullException(nameof(progress));
12 | return true;
13 | }
14 |
15 | public string Descriptor => ":FINAL";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/FirstPassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public class FirstPassSpecifier : IPassSpecifier
7 | {
8 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress)
9 | {
10 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker));
11 | if (progress == null) throw new ArgumentNullException(nameof(progress));
12 | return true;
13 | }
14 |
15 | public string Descriptor => ":FIRST";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/ForPassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public class ForPassSpecifier : IPassSpecifier
7 | {
8 | public readonly string mod;
9 | public readonly UrlDir.UrlConfig urlConfig;
10 |
11 | public ForPassSpecifier(string mod, UrlDir.UrlConfig urlConfig)
12 | {
13 | if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod));
14 | this.mod = mod ?? throw new ArgumentNullException(nameof(mod));
15 | this.urlConfig = urlConfig ?? throw new ArgumentNullException(nameof(urlConfig));
16 | }
17 |
18 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress)
19 | {
20 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker));
21 | if (progress == null) throw new ArgumentNullException(nameof(progress));
22 | bool result = needsChecker.CheckNeeds(mod);
23 | if (!result) progress.NeedsUnsatisfiedFor(urlConfig);
24 | return result;
25 | }
26 |
27 | public string Descriptor => $":FOR[{mod.ToUpper()}]";
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/IPassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public interface IPassSpecifier
7 | {
8 | bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress);
9 | string Descriptor { get; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/InsertPassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public class InsertPassSpecifier : IPassSpecifier
7 | {
8 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress)
9 | {
10 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker));
11 | if (progress == null) throw new ArgumentNullException(nameof(progress));
12 | return true;
13 | }
14 |
15 | public string Descriptor => ":INSERT (initial)";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/LastPassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public class LastPassSpecifier : IPassSpecifier
7 | {
8 | public readonly string mod;
9 |
10 | public LastPassSpecifier(string mod)
11 | {
12 | if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod));
13 | this.mod = mod ?? throw new ArgumentNullException(nameof(mod));
14 | }
15 |
16 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress) => true;
17 | public string Descriptor => $":LAST[{mod.ToUpper()}]";
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PassSpecifiers/LegacyPassSpecifier.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Progress;
3 |
4 | namespace ModuleManager.Patches.PassSpecifiers
5 | {
6 | public class LegacyPassSpecifier : IPassSpecifier
7 | {
8 | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress)
9 | {
10 | if (needsChecker == null) throw new ArgumentNullException(nameof(needsChecker));
11 | if (progress == null) throw new ArgumentNullException(nameof(progress));
12 | return true;
13 | }
14 |
15 | public string Descriptor => ":LEGACY (default)";
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/PatchCompiler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Patches
4 | {
5 | public interface IPatchCompiler
6 | {
7 | IPatch CompilePatch(ProtoPatch protoPatch);
8 | }
9 |
10 | public class PatchCompiler : IPatchCompiler
11 | {
12 | public IPatch CompilePatch(ProtoPatch protoPatch)
13 | {
14 | if (protoPatch == null) throw new ArgumentNullException(nameof(protoPatch));
15 |
16 | return protoPatch.command switch
17 | {
18 | Command.Insert => new InsertPatch(protoPatch.urlConfig, protoPatch.nodeType, protoPatch.passSpecifier),
19 | Command.Edit => new EditPatch(protoPatch.urlConfig, new NodeMatcher(protoPatch.nodeType, protoPatch.nodeName, protoPatch.has), protoPatch.passSpecifier),
20 | Command.Copy => new CopyPatch(protoPatch.urlConfig, new NodeMatcher(protoPatch.nodeType, protoPatch.nodeName, protoPatch.has), protoPatch.passSpecifier),
21 | Command.Delete => new DeletePatch(protoPatch.urlConfig, new NodeMatcher(protoPatch.nodeType, protoPatch.nodeName, protoPatch.has), protoPatch.passSpecifier),
22 | _ => throw new ArgumentException("has an invalid command for a root node: " + protoPatch.command, nameof(protoPatch)),
23 | };
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/ModuleManager/Patches/ProtoPatch.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using ModuleManager.Patches.PassSpecifiers;
3 |
4 | namespace ModuleManager.Patches
5 | {
6 | public class ProtoPatch
7 | {
8 | public readonly UrlDir.UrlConfig urlConfig;
9 | public readonly Command command;
10 | public readonly string nodeType;
11 | public readonly string nodeName;
12 | public readonly string needs = null;
13 | public readonly string has = null;
14 | public readonly IPassSpecifier passSpecifier;
15 |
16 | public ProtoPatch(UrlDir.UrlConfig urlConfig, Command command, string nodeType, string nodeName, string needs, string has, IPassSpecifier passSpecifier)
17 | {
18 | this.urlConfig = urlConfig;
19 | this.command = command;
20 | this.nodeType = nodeType;
21 | this.nodeName = nodeName;
22 | this.needs = needs;
23 | this.has = has;
24 | this.passSpecifier = passSpecifier;
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ModuleManager/Progress/IPatchProgress.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Progress
4 | {
5 | public interface IPatchProgress
6 | {
7 | ProgressCounter Counter { get; }
8 |
9 | float ProgressFraction { get; }
10 |
11 | EventVoid OnPatchApplied { get; }
12 | EventData OnPassStarted { get; }
13 |
14 | void Warning(UrlDir.UrlConfig url, string message);
15 | void Error(UrlDir.UrlConfig url, string message);
16 | void Error(string message);
17 | void Exception(string message, Exception exception);
18 | void Exception(UrlDir.UrlConfig url, string message, Exception exception);
19 | void NeedsUnsatisfiedRoot(UrlDir.UrlConfig url);
20 | void NeedsUnsatisfiedNode(UrlDir.UrlConfig url, string path);
21 | void NeedsUnsatisfiedValue(UrlDir.UrlConfig url, string path);
22 | void NeedsUnsatisfiedBefore(UrlDir.UrlConfig url);
23 | void NeedsUnsatisfiedFor(UrlDir.UrlConfig url);
24 | void NeedsUnsatisfiedAfter(UrlDir.UrlConfig url);
25 | void ApplyingCopy(IUrlConfigIdentifier original, UrlDir.UrlConfig patch);
26 | void ApplyingDelete(IUrlConfigIdentifier original, UrlDir.UrlConfig patch);
27 | void ApplyingUpdate(IUrlConfigIdentifier original, UrlDir.UrlConfig patch);
28 | void PatchAdded();
29 | void PatchApplied();
30 | void PassStarted(IPass pass);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/ModuleManager/Progress/ProgressCounter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using ModuleManager.Utils;
4 |
5 | namespace ModuleManager.Progress
6 | {
7 | public class ProgressCounter
8 | {
9 | public readonly Counter totalPatches = new Counter();
10 | public readonly Counter appliedPatches = new Counter();
11 | public readonly Counter patchedNodes = new Counter();
12 | public readonly Counter warnings = new Counter();
13 | public readonly Counter errors = new Counter();
14 | public readonly Counter exceptions = new Counter();
15 | public readonly Counter needsUnsatisfied = new Counter();
16 |
17 | public readonly Dictionary warningFiles = new Dictionary();
18 | public readonly Dictionary errorFiles = new Dictionary();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ModuleManager/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 |
4 | // Information about this assembly is defined by the following attributes.
5 | // Change them to the values specific to your project.
6 |
7 | [assembly: AssemblyTitle("ModuleManager")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("")]
12 | [assembly: AssemblyCopyright("")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
17 | // The form "{Major}.{Minor}.*" will automatically update the build and revision,
18 | // and "{Major}.{Minor}.{Build}.*" will update just the revision.
19 |
20 | [assembly: AssemblyVersion("4.2.3")]
21 | [assembly: KSPAssembly("ModuleManager", 2, 5)]
22 |
23 | // The following attributes are used to specify the signing key for the assembly,
24 | // if desired. See the Mono documentation for more information about signing.
25 |
26 | //[assembly: AssemblyDelaySign(false)]
27 | //[assembly: AssemblyKeyFile("")]
28 |
29 |
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-1.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-10.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-11.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-12.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-2.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-3.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-4.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-5.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-6.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-7.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-8.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/cat-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/cat-9.png
--------------------------------------------------------------------------------
/ModuleManager/Properties/rainbow2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sarbian/ModuleManager/c4561925f983e7ae81d9dfd4d11356a35cb6b9b6/ModuleManager/Properties/rainbow2.png
--------------------------------------------------------------------------------
/ModuleManager/ProtoUrlConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager
4 | {
5 | public interface IUrlConfigIdentifier
6 | {
7 | string FileUrl { get; }
8 | string NodeType { get; }
9 | string FullUrl { get; }
10 | }
11 |
12 | public interface IProtoUrlConfig : IUrlConfigIdentifier
13 | {
14 | UrlDir.UrlFile UrlFile { get; }
15 | ConfigNode Node { get; }
16 | }
17 |
18 | public class ProtoUrlConfig : IProtoUrlConfig
19 | {
20 | public UrlDir.UrlFile UrlFile { get; }
21 | public ConfigNode Node { get; }
22 | public string FileUrl { get; }
23 | public string NodeType => Node.name;
24 | public string FullUrl { get; }
25 |
26 | public ProtoUrlConfig(UrlDir.UrlFile urlFile, ConfigNode node)
27 | {
28 | UrlFile = urlFile ?? throw new ArgumentNullException(nameof(urlFile));
29 | Node = node ?? throw new ArgumentNullException(nameof(node));
30 | FileUrl = UrlFile.url + '.' + urlFile.fileExtension;
31 | FullUrl = FileUrl + '/' + Node.name;
32 |
33 | if (node.GetValue("name") is string nameValue)
34 | FullUrl += '[' + nameValue + ']';
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/ModuleManager/Tags/Tag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Tags
4 | {
5 | public struct Tag
6 | {
7 | public readonly string key;
8 | public readonly string value;
9 | public readonly string trailer;
10 |
11 | public Tag(string key, string value, string trailer)
12 | {
13 | this.key = key ?? throw new ArgumentNullException(nameof(key));
14 | if (key == string.Empty) throw new ArgumentException("can't be empty", nameof(key));
15 |
16 | if (value == null && trailer != null)
17 | throw new ArgumentException("trailer must be null if value is null");
18 |
19 | if (trailer == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(trailer));
20 |
21 | this.value = value;
22 | this.trailer = trailer;
23 | }
24 |
25 | public override string ToString()
26 | {
27 | string s = "< '" + key + "' ";
28 | if (value != null) s += "[ '" + value + "' ] ";
29 | if (trailer != null) s += "'" + trailer + "' ";
30 | s += ">";
31 | return s;
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/ModuleManager/Tags/TagList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using ModuleManager.Collections;
6 |
7 | namespace ModuleManager.Tags
8 | {
9 | public interface ITagList : IEnumerable
10 | {
11 | Tag PrimaryTag { get; }
12 | }
13 |
14 | public class TagList : ITagList
15 | {
16 | private readonly Tag[] tags;
17 |
18 | public TagList(Tag primaryTag, IEnumerable tags)
19 | {
20 | PrimaryTag = primaryTag;
21 | this.tags = tags?.ToArray() ?? throw new ArgumentNullException(nameof(tags));
22 | }
23 |
24 | public Tag PrimaryTag { get; private set; }
25 |
26 | public ArrayEnumerator GetEnumerator() => new ArrayEnumerator(tags);
27 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
28 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/ModuleManager/Threading/BackgroundTask.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 |
4 | namespace ModuleManager.Threading
5 | {
6 | public static class BackgroundTask
7 | {
8 | public static ITaskStatus Start(Action action)
9 | {
10 | if (action == null) throw new ArgumentNullException(nameof(action));
11 |
12 | TaskStatus status = new TaskStatus();
13 |
14 | void RunAction()
15 | {
16 | try
17 | {
18 | action();
19 | status.Finished();
20 | }
21 | catch (Exception ex)
22 | {
23 | status.Error(ex);
24 | }
25 | }
26 |
27 | Thread thread = new Thread(RunAction);
28 | thread.Start();
29 |
30 | return new TaskStatusWrapper(status);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/ModuleManager/Threading/ITaskStatus.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Threading
4 | {
5 | public interface ITaskStatus
6 | {
7 | bool IsRunning { get; }
8 | bool IsFinished { get; }
9 | bool IsExitedWithError { get; }
10 | Exception Exception { get; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ModuleManager/Threading/TaskStatus.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Threading
4 | {
5 | public class TaskStatus : ITaskStatus
6 | {
7 | private readonly object lockObject = new object();
8 |
9 | public bool IsRunning { get; private set; } = true;
10 | public Exception Exception { get; private set; } = null;
11 |
12 | public bool IsFinished
13 | {
14 | get
15 | {
16 | lock (lockObject)
17 | {
18 | return !IsRunning && Exception == null;
19 | }
20 | }
21 | }
22 |
23 | public bool IsExitedWithError
24 | {
25 | get
26 | {
27 | lock (lockObject)
28 | {
29 | return !IsRunning && Exception != null;
30 | }
31 | }
32 | }
33 |
34 | public void Finished()
35 | {
36 | lock (lockObject)
37 | {
38 | if (!IsRunning) throw new InvalidOperationException("Task is not running");
39 | IsRunning = false;
40 | }
41 | }
42 |
43 | public void Error(Exception exception)
44 | {
45 | lock(lockObject)
46 | {
47 | if (!IsRunning) throw new InvalidOperationException("Task is not running");
48 | this.Exception = exception ?? throw new ArgumentNullException(nameof(exception));
49 | IsRunning = false;
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/ModuleManager/Threading/TaskStatusWrapper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Threading
4 | {
5 | public class TaskStatusWrapper : ITaskStatus
6 | {
7 | private readonly ITaskStatus inner;
8 |
9 | public TaskStatusWrapper(ITaskStatus inner)
10 | {
11 | this.inner = inner;
12 | }
13 |
14 | public bool IsRunning => inner.IsRunning;
15 | public bool IsFinished => inner.IsFinished;
16 | public bool IsExitedWithError => inner.IsExitedWithError;
17 | public Exception Exception => inner.Exception;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/ModuleManager/Utils/Counter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ModuleManager.Utils
4 | {
5 | public class Counter
6 | {
7 | public int Value { get; private set; } = 0;
8 |
9 | public void Increment()
10 | {
11 | Value++;
12 | }
13 |
14 | public override string ToString()
15 | {
16 | return Value.ToString();
17 | }
18 |
19 | public static implicit operator int(Counter counter) => counter.Value;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ModuleManager/Utils/FileUtils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Security.Cryptography;
4 | using ModuleManager.Extensions;
5 |
6 | namespace ModuleManager.Utils
7 | {
8 | public static class FileUtils
9 | {
10 | public static string FileSHA(string filename)
11 | {
12 | if (!File.Exists(filename)) throw new FileNotFoundException("File does not exist", filename);
13 |
14 | using SHA256 sha = SHA256.Create();
15 | using FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
16 | byte[] data = sha.ComputeHash(fs);
17 |
18 | return data.ToHex();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ModuleManager/copy_build.sh:
--------------------------------------------------------------------------------
1 | if [ -z "${TARGET_PATH}" ] ; then
2 | echo 'Expected $TARGET_PATH to be defined but it is not' >&2
3 | exit 1
4 | elif ! [ -f "${TARGET_PATH}" ] ; then
5 | echo 'Expected $TARGET_PATH to be a file but it is not' >&2
6 | exit 1
7 | fi
8 |
9 | if [ -z "${TARGET_DIR}" ] ; then
10 | echo 'Expected $TARGET_DIR to be defined but it is not' >&2
11 | exit 1
12 | elif ! [ -d "${TARGET_DIR}" ] ; then
13 | echo 'Expected $TARGET_DIR to be a directory but it is not' >&2
14 | exit 1
15 | fi
16 |
17 | if [ -z "${TARGET_NAME}" ] ; then
18 | echo 'Expected $TARGET_NAME to be defined but it is not' >&2
19 | exit 1
20 | fi
21 |
22 | if [ -z "${PDB2MDB}" ] ; then
23 | echo '$PDB2MDB not found'
24 | else
25 | echo "Running '${PDB2MDB}'"
26 | "${PDB2MDB}" "${TARGET_PATH}"
27 | fi
28 |
29 | if [ -z "${KSPDIR}" ] ; then
30 | echo '$KSPDIR not found'
31 | else
32 | if ! [ -d "${KSPDIR}" ] ; then
33 | echo 'Expected $KSPDIR to point to a directory but it is not' >&2
34 | exit 1
35 | fi
36 | if ! [ -d "${KSPDIR}/GameData" ] ; then
37 | echo 'Expected $KSPDIR to contain a GameData subdirectory but it does not' >&2
38 | exit 1
39 | fi
40 | echo "Copying to '${KSPDIR}'"
41 | cp "${TARGET_PATH}" "${KSPDIR}/GameData/"
42 | test -f "${TARGET_DIR}/${TARGET_NAME}.pdb" && cp "${TARGET_DIR}/${TARGET_NAME}.pdb" "${KSPDIR}/GameData/"
43 | test -f "${TARGET_DIR}/${TARGET_NAME}.dll.mdb" && cp "${TARGET_DIR}/${TARGET_NAME}.dll.mdb" "${KSPDIR}/GameData/"
44 | rm -f "${KSPDIR}/GameData/ModuleManager.ConfigCache"
45 | fi
46 |
--------------------------------------------------------------------------------
/ModuleManager/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/ModuleManagerTests/Collections/KeyValueCacheTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Xunit;
3 | using ModuleManager.Collections;
4 |
5 | namespace ModuleManagerTests.Collections
6 | {
7 | public class KeyValueCacheTest
8 | {
9 | [Fact]
10 | public void TestFetch__CreateValueNull()
11 | {
12 | KeyValueCache