Showing preview only (669K chars total). Download the full file or copy to clipboard to get everything.
Repository: sarbian/ModuleManager
Branch: master
Commit: c4561925f983
Files: 175
Total size: 620.3 KB
Directory structure:
gitextract_882ubem8/
├── .gitignore
├── CopyLocalFalse.txt
├── Makefile
├── 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
│ ├── 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
├── ModuleManager.sln
├── ModuleManager.sln.DotSettings
├── 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
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
[Oo]bj/
# mstest test results
TestResults
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user*
*.sln.docstates
# Build results
[Dd]ebug/
[Rr]elease/
x64/
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.log
*.vspscc
*.vssscc
.builds
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish
# Publish Web Output
*.Publish.xml
# NuGet Packages Directory
packages
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
[Bb]in
[Oo]bj
sql
TestResults
[Tt]est[Rr]esult*
*.Cache
ClientBin
[Ss]tyle[Cc]op.*
~$*
*.dbmdl
Generated_Code #added for RIA/Silverlight projects
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
ModuleManager.csproj.user
ModuleManager.*.dll
.vs*
================================================
FILE: CopyLocalFalse.txt
================================================
Copy Local has been set to false for all dependencies.
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!)
================================================
FILE: Makefile
================================================
# Makefile for building ModuleManager
KSPDIR := ${HOME}/.local/share/Steam/SteamApps/common/Kerbal\ Space\ Program
MANAGED := KSP_Data/Managed/
INCLUDEFILES := $(wildcard *.cs) \
$(wildcard Properties/*.cs)
RESGEN2 := resgen2
GMCS := /usr/bin/gmcs
GIT := /usr/bin/git
TAR := /usr/bin/tar
ZIP := /usr/bin/zip
all: build
info:
@echo "== ModuleManager Build Information =="
@echo " gmcs: ${GMCS}"
@echo " git: ${GIT}"
@echo " tar: ${TAR}"
@echo " zip: ${ZIP}"
@echo " KSP Data: ${KSPDIR}"
@echo "====================================="
build: info
mkdir -p build
${RESGEN2} -usesourcepath Properties/Resources.resx build/Resources.resources
${GMCS} -t:library -lib:${KSPDIR}/${MANAGED} \
-r:Assembly-CSharp,Assembly-CSharp-firstpass,UnityEngine \
-out:build/ModuleManager.dll \
-resource:build/Resources.resources,ModuleManager.Properties.Resources.resources \
${INCLUDEFILES}
tar.gz: build
${TAR} zcf ModuleManager-0.$(shell ${GIT} rev-list --count HEAD).g$(shell ${GIT} log -1 --format="%h").tar.gz build/ModuleManager.dll
zip: build
${ZIP} -9 -r ModuleManager-0.$(shell ${GIT} rev-list --count HEAD).g$(shell ${GIT} log -1 --format="%h").zip build/ModuleManager.dll
clean:
@echo "Cleaning up build and package directories..."
rm -rf build/ package/
install: build
mkdir -p ${KSPDIR}/GameData/
cp build/ModuleManager.dll ${KSPDIR}/GameData/
uninstall: info
rm -f ${KSPDIR}/GameData/ModuleManager.dll
.PHONY : all info build tar.gz zip clean install uninstall
================================================
FILE: ModuleManager/Cats/CatAnimator.cs
================================================
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
namespace ModuleManager.Cats
{
class CatAnimator : MonoBehaviour
{
public Sprite[] frames;
public float secFrame = 0.07f;
private SpriteRenderer spriteRenderer;
private int spriteIdx;
[SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
void Start()
{
spriteRenderer = GetComponent<SpriteRenderer>();
spriteRenderer.sortingOrder = 3;
StartCoroutine(Animate());
}
IEnumerator Animate()
{
if (frames.Length == 0)
yield return null;
WaitForSeconds yield = new WaitForSeconds(secFrame);
while (true)
{
spriteIdx = (spriteIdx + 1) % frames.Length;
spriteRenderer.sprite = frames[spriteIdx];
yield return yield;
}
}
}
}
================================================
FILE: ModuleManager/Cats/CatManager.cs
================================================
using System;
using UnityEngine;
namespace ModuleManager.Cats
{
public static class CatManager
{
private static Sprite[] catFrames;
private static Texture2D rainbow;
private static int scale = 1;
public static void LaunchCat()
{
InitCats();
GameObject cat = LaunchCat(scale);
cat.AddComponent<CatMover>();
}
public static void LaunchCats()
{
InitCats();
GameObject catSun = LaunchCat(scale);
CatOrbiter catSunOrbiter = catSun.AddComponent<CatOrbiter>();
catSunOrbiter.Init(null, 0);
int cats = UnityEngine.Random.Range(6, 10);
for (int i = 0; i < cats; i++)
{
GameObject cat = LaunchCat(scale);
CatOrbiter catOrbiter = cat.AddComponent<CatOrbiter>();
catOrbiter.Init(catSunOrbiter, Screen.height * 0.5f);
int moons = UnityEngine.Random.Range(0, 4);
for (int j = 0; j < moons; j++)
{
GameObject catMoon = LaunchCat(scale);
CatOrbiter catMoonOrbiter = catMoon.AddComponent<CatOrbiter>();
catMoonOrbiter.Init(catOrbiter, Screen.height * 0.06f);
}
}
}
private static void InitCats()
{
Texture2D[] tex = new Texture2D[12];
for (int i = 0; i < tex.Length; i++)
{
tex[i] = new Texture2D(70, 42, TextureFormat.ARGB32, false);
}
tex[0].LoadImage(Properties.Resources.cat1);
tex[1].LoadImage(Properties.Resources.cat2);
tex[2].LoadImage(Properties.Resources.cat3);
tex[3].LoadImage(Properties.Resources.cat4);
tex[4].LoadImage(Properties.Resources.cat5);
tex[5].LoadImage(Properties.Resources.cat6);
tex[6].LoadImage(Properties.Resources.cat7);
tex[7].LoadImage(Properties.Resources.cat8);
tex[8].LoadImage(Properties.Resources.cat9);
tex[9].LoadImage(Properties.Resources.cat10);
tex[10].LoadImage(Properties.Resources.cat11);
tex[11].LoadImage(Properties.Resources.cat12);
rainbow = new Texture2D(39, 36, TextureFormat.ARGB32, false);
rainbow.LoadImage(Properties.Resources.rainbow);
rainbow.Apply();
catFrames = new Sprite[12];
for (int i = 0; i < tex.Length; i++)
{
tex[i].Apply();
catFrames[i] = Sprite.Create(tex[i], new Rect(0, 0, tex[i].width, tex[i].height), new Vector2(.5f, .5f));
catFrames[i].name = "cat" + i;
}
scale = 1;
if (Screen.height >= 1080)
scale *= 2;
if (Screen.height > 1440)
scale *= 3;
Physics2D.gravity = Vector2.zero;
}
private static GameObject LaunchCat(int scale)
{
GameObject cat = new GameObject("NyanCat");
SpriteRenderer sr = cat.AddComponent<SpriteRenderer>();
TrailRenderer trail = cat.AddComponent<TrailRenderer>();
CatAnimator catAnimator = cat.AddComponent<CatAnimator>();
sr.sprite = catFrames[0];
trail.material = new Material(Shader.Find("Legacy Shaders/Particles/Alpha Blended"));
Debug.Log("material = " + trail.material);
trail.material.mainTexture = rainbow;
trail.time = 1.5f;
trail.startWidth = 0.6f * scale * rainbow.height;
trail.endColor = Color.white.A(0.1f);
trail.colorGradient = new Gradient {alphaKeys = new GradientAlphaKey[3] { new GradientAlphaKey(1, 0), new GradientAlphaKey(1, 0.75f), new GradientAlphaKey(0.2f, 1) }};
trail.Clear();
cat.layer = LayerMask.NameToLayer("UI");
catAnimator.frames = catFrames;
cat.transform.localScale = 70 * scale * Vector3.one;
return cat;
}
}
}
================================================
FILE: ModuleManager/Cats/CatMover.cs
================================================
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
namespace ModuleManager.Cats
{
public class CatMover : MonoBehaviour
{
public Vector3 spos;
public float vel = 5;
private float offsetY;
public TrailRenderer trail;
private SpriteRenderer spriteRenderer;
private int totalLenth = 100;
private float activePos = 0;
public float scale = 2;
private const float time = 5;
private const float trailTime = time / 4;
private bool clearTrail = false;
// Use this for initialization
[SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
void Start()
{
trail = GetComponent<TrailRenderer>();
trail.sortingOrder = 2;
spriteRenderer = GetComponent<SpriteRenderer>();
offsetY = Mathf.FloorToInt(0.2f * Screen.height);
spos.z = -1;
totalLenth = (int) (Screen.width / time * trail.time) + 150;
trail.time = trailTime;
trail.widthCurve = new AnimationCurve(new Keyframe(0, trail.startWidth ), new Keyframe(0.7f, trail.startWidth), new Keyframe(1, trail.startWidth * 0.9f));
clearTrail = true;
}
[SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
void Update()
{
if (trail.time <= 0f)
{
trail.time = trailTime;
}
activePos += ((Screen.width / time) * Time.deltaTime);
if (activePos > (Screen.width + totalLenth))
{
activePos = -spriteRenderer.sprite.rect.width;
clearTrail = true;
}
float f = 2f * Mathf.PI * (activePos) / (Screen.width * 0.5f);
float heightOffset = Mathf.Sin(f) * (spriteRenderer.sprite.rect.height * scale);
spos.x = activePos;
spos.y = offsetY + heightOffset;
transform.position = KSP.UI.UIMainCamera.Camera.ScreenToWorldPoint(spos);
transform.rotation = Quaternion.Euler(0, 0, Mathf.Cos(f) * 0.25f * Mathf.PI * Mathf.Rad2Deg);
if (clearTrail)
{
trail.Clear();
clearTrail = false;
}
}
}
}
================================================
FILE: ModuleManager/Cats/CatOrbiter.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using KSP.UI;
using UnityEngine;
using Random = UnityEngine.Random;
namespace ModuleManager.Cats
{
class CatOrbiter : MonoBehaviour
{
private static readonly List<CatOrbiter> orbiters = new List<CatOrbiter>();
private static CatOrbiter sun;
private double _mass;
public Rigidbody2D rb;
private Vector2d pos;
private Vector2d vel;
private Vector2d force;
private float scale = 1;
private const double G = 6.67408E-11;
public double Mass
{
get { return _mass; }
set
{
_mass = value;
if (rb!=null)
rb.mass = (float)_mass;
}
}
public void Init(CatOrbiter parent, float soi)
{
TimingManager.FixedUpdateAdd(TimingManager.TimingStage.Earlyish, DoForces);
orbiters.Add(this);
rb = gameObject.AddComponent<Rigidbody2D>();
rb.isKinematic = true;
if (orbiters.Count == 1)
{
sun = this;
Vector3 spos = new Vector3(Screen.width * 0.5f, Screen.height * 0.5f, -1);
transform.position = KSP.UI.UIMainCamera.Camera.ScreenToWorldPoint(spos);
Mass = 2E17;
pos.x = transform.position.x;
pos.y = transform.position.y;
}
else
{
Vector2 relativePos = Random.insideUnitCircle;
if (relativePos.magnitude < 0.2)
relativePos = relativePos.normalized * 0.3f;
Vector3 spos = UIMainCamera.Camera.WorldToScreenPoint(parent.transform.position) + (Vector3)(relativePos * soi);
spos.z = -1;
transform.position = UIMainCamera.Camera.ScreenToWorldPoint(spos);
pos.x = transform.position.x;
pos.y = transform.position.y;
//int scaleRange = 10;
//
//float factor = (1 + (scaleRange - 1) * Random.value);
//
//scale = parent.scale * factor / scaleRange;
scale = parent.scale * 0.6f;
transform.localScale *= scale;
TrailRenderer trail = gameObject.GetComponent<TrailRenderer>();
trail.colorGradient = new Gradient() {alphaKeys = new GradientAlphaKey[3] { new GradientAlphaKey(1, 0), new GradientAlphaKey(1, 0.7f), new GradientAlphaKey(0, 1) }};
trail.startWidth *= scale;
//trail.endWidth *= scale;
trail.widthCurve = new AnimationCurve(new Keyframe(0, trail.startWidth ), new Keyframe(0.7f, trail.startWidth), new Keyframe(1, trail.startWidth * 0.9f));
//Mass = factor * 2E16;
Mass = parent.Mass * 0.025;
Vector2d dist = parent.pos - pos;
double circularVel = Math.Sqrt(G * (Mass + parent.Mass) / dist.magnitude);
if (parent == sun)
circularVel *= Random.Range(0.9f, 1.1f);
Debug.Log("CatOrbiter " + circularVel.ToString("F3") + " " + Mass.ToString("F2") + " " + orbiters[0].Mass.ToString("F2") + " " +
dist.magnitude.ToString("F2"));
Vector3d normal = (Random.value >= 0.3) ? Vector3d.back : Vector3d.forward;
Vector3d vel3d = Vector3d.Cross(dist, normal).normalized * circularVel;
vel.x = parent.vel.x + vel3d.x;
vel.y = parent.vel.y + vel3d.y;
}
rb.MovePosition(new Vector2((float)pos.x, (float)pos.y));
}
private void DoForces()
{
force = Vector2d.zero;
foreach (CatOrbiter cat in orbiters)
{
if (cat == this)
continue;
// F = G * (m1 * m2) / r^2
Vector2d dir = cat.pos - pos;
double f = G * (cat.Mass * Mass) / (dir.sqrMagnitude + 10); // +10 to avoid div/0
force += (float)f * dir.normalized;
}
}
[SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
void OnDestroy()
{
orbiters.Remove(this);
TimingManager.FixedUpdateRemove(TimingManager.TimingStage.Earlyish, DoForces);
}
[SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
void FixedUpdate()
{
//if (this == sun)
// return;
vel += Time.fixedDeltaTime * force / Mass;
pos += Time.fixedDeltaTime * vel;
rb.MovePosition(new Vector2((float)pos.x, (float)pos.y));
double angle = Math.Atan2(vel.y, vel.x) * Mathf.Rad2Deg;
rb.MoveRotation((float)angle);
}
}
}
================================================
FILE: ModuleManager/Collections/ArrayEnumerator.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
namespace ModuleManager.Collections
{
public struct ArrayEnumerator<T> : IEnumerator<T>
{
private readonly T[] array;
private readonly int startIndex;
private readonly int length;
private int index;
public ArrayEnumerator(params T[] array) : this(array, 0) { }
public ArrayEnumerator(T[] array, int startIndex) : this(array, startIndex, (array?.Length ?? -1) - startIndex) { }
public ArrayEnumerator(T[] array, int startIndex, int length)
{
this.array = array ?? throw new ArgumentNullException(nameof(array));
if (startIndex < 0)
throw new ArgumentException($"must be non-negative (got {startIndex})", nameof(startIndex));
if (startIndex > array.Length)
throw new ArgumentException(
$"must be less than or equal to array length (array length {array.Length}, startIndex {startIndex})",
nameof(startIndex)
);
if (length < 0)
throw new ArgumentException($"must be non-negative (got {length})", nameof(length));
if (startIndex + length > array.Length)
throw new ArgumentException(
$"must fit within the string (array length {array.Length}, startIndex {startIndex}, length {length})",
nameof(length)
);
this.startIndex = startIndex;
this.length = length;
index = startIndex - 1;
}
public T Current => array[index];
object IEnumerator.Current => Current;
public void Dispose() { }
public bool MoveNext()
{
index++;
return index < startIndex + length;
}
public void Reset()
{
index = startIndex - 1;
}
}
}
================================================
FILE: ModuleManager/Collections/ImmutableStack.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
namespace ModuleManager.Collections
{
public class ImmutableStack<T> : IEnumerable<T>
{
public struct Enumerator : IEnumerator<T>
{
private readonly ImmutableStack<T> head;
private ImmutableStack<T> currentStack;
public Enumerator(ImmutableStack<T> stack)
{
head = stack;
currentStack = null;
}
public T Current => currentStack.value;
object IEnumerator.Current => Current;
public void Dispose() { }
public bool MoveNext()
{
if (currentStack == null)
{
currentStack = head;
return true;
}
else if (!currentStack.IsRoot)
{
currentStack = currentStack.parent;
return true;
}
else
{
return false;
}
}
public void Reset() => currentStack = null;
}
public readonly T value;
public readonly ImmutableStack<T> parent;
public ImmutableStack(T value)
{
this.value = value;
}
private ImmutableStack(T value, ImmutableStack<T> parent)
{
this.value = value;
this.parent = parent;
}
public bool IsRoot => parent == null;
public ImmutableStack<T> Root => IsRoot? this : parent.Root;
public int Depth => IsRoot ? 1 : parent.Depth + 1;
public ImmutableStack<T> Push(T newValue)
{
return new ImmutableStack<T>(newValue, this);
}
public ImmutableStack<T> Pop()
{
if (IsRoot) throw new InvalidOperationException("Cannot pop from the root of a stack");
return parent;
}
public ImmutableStack<T> ReplaceValue(T newValue) => new ImmutableStack<T>(newValue, parent);
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
================================================
FILE: ModuleManager/Collections/KeyValueCache.cs
================================================
using System;
using System.Collections.Generic;
namespace ModuleManager.Collections
{
public class KeyValueCache<TKey, TValue>
{
private readonly Dictionary<TKey, TValue> dict = new Dictionary<TKey, TValue>();
private readonly object lockObject = new object();
public TValue Fetch(TKey key, Func<TValue> createValue)
{
if (createValue == null) throw new ArgumentNullException(nameof(createValue));
lock(lockObject)
{
if (dict.TryGetValue(key, out TValue value))
{
return value;
}
else
{
TValue newValue = createValue();
dict.Add(key, newValue);
return newValue;
}
}
}
}
}
================================================
FILE: ModuleManager/Collections/MessageQueue.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
namespace ModuleManager.Collections
{
public interface IMessageQueue<T> : IEnumerable<T>
{
void Add(T value);
IMessageQueue<T> TakeAll();
}
public class MessageQueue<T> : IMessageQueue<T>, IEnumerable<T>
{
public sealed class Enumerator : IEnumerator<T>
{
private readonly MessageQueue<T> queue;
private Node current;
public Enumerator(MessageQueue<T> queue)
{
this.queue = queue;
}
public T Current => current.value;
object IEnumerator.Current => Current;
public void Dispose() { }
public bool MoveNext()
{
if (current == null)
current = queue.head;
else
current = current.next;
return current != null;
}
public void Reset()
{
current = null;
}
}
private class Node
{
public Node next;
public readonly T value;
public Node(T value)
{
this.value = value;
}
}
private readonly object lockObject = new object();
private Node head;
private Node tail;
public void Add(T value)
{
Node node = new Node(value);
lock (lockObject)
{
if (head == null)
{
head = node;
tail = node;
}
else
{
tail.next = node;
tail = node;
}
}
}
public IMessageQueue<T> TakeAll()
{
MessageQueue<T> queue = new MessageQueue<T>();
lock(lockObject)
{
queue.head = head;
queue.tail = tail;
head = null;
tail = null;
}
return queue;
}
public Enumerator GetEnumerator() => new Enumerator(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
================================================
FILE: ModuleManager/Command.cs
================================================
using System;
namespace ModuleManager
{
public enum Command
{
Insert,
Delete,
Edit,
Replace,
Copy,
Rename,
Paste,
Special,
Create
}
}
================================================
FILE: ModuleManager/CommandParser.cs
================================================
using System;
namespace ModuleManager
{
public static class CommandParser
{
public static Command Parse(string name, out string valueName)
{
if (name.Length == 0)
{
valueName = string.Empty;
return Command.Insert;
}
Command ret;
switch (name[0])
{
case '@':
ret = Command.Edit;
break;
case '%':
ret = Command.Replace;
break;
case '-':
case '!':
ret = Command.Delete;
break;
case '+':
case '$':
ret = Command.Copy;
break;
case '|':
ret = Command.Rename;
break;
case '#':
ret = Command.Paste;
break;
case '*':
ret = Command.Special;
break;
case '&':
ret = Command.Create;
break;
default:
valueName = name;
return Command.Insert;
}
valueName = name.Substring(1);
return ret;
}
}
}
================================================
FILE: ModuleManager/CustomConfigsManager.cs
================================================
using System;
using System.IO;
using UnityEngine;
using static ModuleManager.FilePathRepository;
namespace ModuleManager
{
[KSPAddon(KSPAddon.Startup.SpaceCentre, false)]
public class CustomConfigsManager : MonoBehaviour
{
internal void Start()
{
if (HighLogic.CurrentGame.Parameters.Career.TechTreeUrl != techTreeFile && File.Exists(techTreePath))
{
Log("Setting modded tech tree as the active one");
HighLogic.CurrentGame.Parameters.Career.TechTreeUrl = techTreeFile;
}
}
public static void Log(String s)
{
print("[CustomConfigsManager] " + s);
}
}
}
================================================
FILE: ModuleManager/ExceptionIntercept/InterceptLogHandler.cs
================================================
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UnityEngine;
using Object = UnityEngine.Object;
namespace ModuleManager.UnityLogHandle
{
class InterceptLogHandler : ILogHandler
{
private readonly ILogHandler baseLogHandler;
private readonly List<Assembly> brokenAssemblies = new List<Assembly>();
private readonly int gamePathLength;
public static string Warnings { get; private set; } = "";
public InterceptLogHandler(ILogHandler baseLogHandler)
{
this.baseLogHandler = baseLogHandler ?? throw new ArgumentNullException(nameof(baseLogHandler));
gamePathLength = Path.GetFullPath(KSPUtil.ApplicationRootPath).Length;
}
public void LogFormat(LogType logType, Object context, string format, params object[] args)
{
baseLogHandler.LogFormat(logType, context, format, args);
}
public void LogException(Exception exception, Object context)
{
baseLogHandler.LogException(exception, context);
if (exception is ReflectionTypeLoadException ex)
{
string message = "Intercepted a ReflectionTypeLoadException. List of broken DLLs:\n";
try
{
var assemblies = ex.Types.Where(x => x != null).Select(x => x.Assembly).Distinct();
foreach (Assembly assembly in assemblies)
{
if (Warnings == "")
{
Warnings = "Mod(s) DLL that are not compatible with this version of KSP\n";
}
string modInfo = assembly.GetName().Name + " " + assembly.GetName().Version + " " +
assembly.Location.Remove(0, gamePathLength) + "\n";
if (!brokenAssemblies.Contains(assembly))
{
brokenAssemblies.Add(assembly);
Warnings += modInfo;
}
message += modInfo;
}
}
catch (Exception e)
{
message += "Exception " + e.GetType().Name + " while handling the exception...";
}
ModuleManager.Log(message);
}
}
}
}
================================================
FILE: ModuleManager/Extensions/ByteArrayExtensions.cs
================================================
using System;
namespace ModuleManager.Extensions
{
public static class ByteArrayExtensions
{
public static string ToHex(this byte[] data)
{
if (data == null) throw new ArgumentNullException(nameof(data));
char[] result = new char[data.Length * 2];
for (int i = 0; i < data.Length; i++)
{
result[i * 2] = GetHexValue(data[i] / 16);
result[i * 2 + 1] = GetHexValue(data[i] % 16);
}
return new string(result);
}
private static char GetHexValue(int i)
{
if (i < 10)
return (char)(i + '0');
else
return (char)(i - 10 + 'a');
}
}
}
================================================
FILE: ModuleManager/Extensions/ConfigNodeExtensions.cs
================================================
using System;
using System.Text;
namespace ModuleManager.Extensions
{
public static class ConfigNodeExtensions
{
public static void ShallowCopyFrom(this ConfigNode toNode, ConfigNode fromeNode)
{
toNode.ClearData();
foreach (ConfigNode.Value value in fromeNode.values)
toNode.values.Add(value);
foreach (ConfigNode node in fromeNode.nodes)
toNode.nodes.Add(node);
}
// KSP implementation of ConfigNode.CreateCopy breaks with badly formed nodes (nodes with a blank name)
public static ConfigNode DeepCopy(this ConfigNode from)
{
ConfigNode to = new ConfigNode(from.name);
foreach (ConfigNode.Value value in from.values)
to.AddValueSafe(value.name, value.value);
foreach (ConfigNode node in from.nodes)
{
ConfigNode newNode = DeepCopy(node);
to.nodes.Add(newNode);
}
return to;
}
public static void PrettyPrint(this ConfigNode node, ref StringBuilder sb, string indent)
{
if (sb == null) throw new ArgumentNullException(nameof(sb));
if (indent == null) indent = string.Empty;
if (node == null)
{
sb.Append(indent + "<null node>\n");
return;
}
sb.AppendFormat("{0}{1}\n{2}{{\n", indent, node.name ?? "<null>", indent);
string newindent = indent + " ";
if (node.values == null)
{
sb.AppendFormat("{0}<null value list>\n", newindent);
}
else
{
foreach (ConfigNode.Value value in node.values)
{
if (value == null)
sb.AppendFormat("{0}<null value>\n", newindent);
else
sb.AppendFormat("{0}{1} = {2}\n", newindent, value.name ?? "<null>", value.value ?? "<null>");
}
}
if (node.nodes == null)
{
sb.AppendFormat("{0}<null node list>\n", newindent);
}
else
{
foreach (ConfigNode subnode in node.nodes)
{
subnode.PrettyPrint(ref sb, newindent);
}
}
sb.AppendFormat("{0}}}\n", indent);
}
public static void AddValueSafe(this ConfigNode node, string name, string value)
{
node.values.Add(new ConfigNode.Value(name, value));
}
public static void EscapeValuesRecursive(this ConfigNode theNode)
{
foreach (ConfigNode subNode in theNode.nodes)
{
subNode.EscapeValuesRecursive();
}
foreach (ConfigNode.Value value in theNode.values)
{
value.value = value.value.Replace("\n", "\\n");
value.value = value.value.Replace("\r", "\\r");
value.value = value.value.Replace("\t", "\\t");
}
}
public static void UnescapeValuesRecursive(this ConfigNode theNode)
{
foreach (ConfigNode subNode in theNode.nodes)
{
subNode.UnescapeValuesRecursive();
}
foreach (ConfigNode.Value value in theNode.values)
{
value.value = value.value.Replace("\\n", "\n");
value.value = value.value.Replace("\\r", "\r");
value.value = value.value.Replace("\\t", "\t");
}
}
}
}
================================================
FILE: ModuleManager/Extensions/IBasicLoggerExtensions.cs
================================================
using System;
using UnityEngine;
using ModuleManager.Logging;
namespace ModuleManager.Extensions
{
public static class IBasicLoggerExtensions
{
public static void Info(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Log, message));
public static void Warning(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Warning, message));
public static void Error(this IBasicLogger logger, string message) => logger.Log(new LogMessage(LogType.Error, message));
public static void Exception(this IBasicLogger logger, Exception exception)
{
if (exception == null) throw new ArgumentNullException(nameof(exception));
logger.Log(new LogMessage(LogType.Exception, exception.ToString()));
}
public static void Exception(this IBasicLogger logger, string message, Exception exception)
{
if (message == null) throw new ArgumentNullException(nameof(message));
if (exception == null) throw new ArgumentNullException(nameof(exception));
logger.Log(new LogMessage(LogType.Exception, message + ": " + exception.ToString()));
}
}
}
================================================
FILE: ModuleManager/Extensions/NodeStackExtensions.cs
================================================
using System;
using System.Linq;
using System.Text;
using NodeStack = ModuleManager.Collections.ImmutableStack<ConfigNode>;
namespace ModuleManager.Extensions
{
public static class NodeStackExtensions
{
public static string GetPath(this NodeStack stack)
{
int length = stack.Sum(node => node.name.Length) + stack.Depth - 1;
StringBuilder sb = new StringBuilder(length);
foreach (ConfigNode node in stack)
{
string nodeName = node.name;
sb.Insert(0, node.name);
if (sb.Length < sb.Capacity) sb.Insert(0, '/');
}
return sb.ToString();
}
}
}
================================================
FILE: ModuleManager/Extensions/StringExtensions.cs
================================================
using System;
using System.Text.RegularExpressions;
namespace ModuleManager.Extensions
{
public static class StringExtensions
{
public static bool IsBracketBalanced(this string s)
{
int level = 0;
foreach (char c in s)
{
if (c == '[') level++;
else if (c == ']') level--;
if (level < 0) return false;
}
return level == 0;
}
private static readonly Regex whitespaceRegex = new Regex(@"\s+");
public static string RemoveWS(this string withWhite)
{
return whitespaceRegex.Replace(withWhite, "");
}
public static bool Contains(this string str, string value, out int index)
{
if (str == null) throw new ArgumentNullException(nameof(str));
if (value == null) throw new ArgumentNullException(nameof(value));
index = str.IndexOf(value, StringComparison.CurrentCultureIgnoreCase);
return index != -1;
}
}
}
================================================
FILE: ModuleManager/Extensions/UrlConfigExtensions.cs
================================================
using System;
using System.Text;
namespace ModuleManager.Extensions
{
public static class UrlConfigExtensions
{
public static string SafeUrl(this UrlDir.UrlConfig url)
{
if (url == null) return "<null>";
string nodeName;
if (!string.IsNullOrEmpty(url.type?.Trim()))
{
nodeName = url.type;
}
else if (url.type == null)
{
nodeName = "<null>";
}
else
{
nodeName = "<blank>";
}
string parentUrl = null;
if (url.parent != null)
{
try
{
parentUrl = url.parent.url;
}
catch
{
parentUrl = "<unknown>";
}
}
if (parentUrl == null)
return nodeName;
else
return parentUrl + "/" + nodeName;
}
public static string PrettyPrint(this UrlDir.UrlConfig config)
{
if (config == null) return "<null UrlConfig>";
StringBuilder sb = new StringBuilder();
sb.Append(config.SafeUrl());
sb.Append('\n');
config.config.PrettyPrint(ref sb, " ");
return sb.ToString();
}
}
}
================================================
FILE: ModuleManager/Extensions/UrlDirExtensions.cs
================================================
using System;
using System.Linq;
namespace ModuleManager.Extensions
{
public static class UrlDirExtensions
{
public static UrlDir.UrlFile Find(this UrlDir urlDir, string url)
{
if (urlDir == null) throw new ArgumentNullException(nameof(urlDir));
if (url == null) throw new ArgumentNullException(nameof(url));
string[] splits = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
UrlDir currentDir = urlDir;
for (int i = 0; i < splits.Length - 1; i++)
{
currentDir = currentDir.children.FirstOrDefault(subDir => subDir.name == splits[i]);
if (currentDir == null) return null;
}
string fileName = splits[splits.Length - 1];
string fileExtension = null;
int idx = fileName.LastIndexOf('.');
if (idx > -1)
{
fileExtension = fileName.Substring(idx + 1);
fileName = fileName.Substring(0, idx);
}
foreach (UrlDir.UrlFile file in currentDir.files)
{
if (file.name != fileName) continue;
if (fileExtension != null && fileExtension != file.fileExtension) continue;
return file;
}
return null;
}
}
}
================================================
FILE: ModuleManager/Extensions/UrlFileExtensions.cs
================================================
using System;
namespace ModuleManager.Extensions
{
public static class UrlFileExtensions
{
public static string GetUrlWithExtension(this UrlDir.UrlFile urlFile)
{
return $"{urlFile.url}.{urlFile.fileExtension}";
}
public static string GetNameWithExtension(this UrlDir.UrlFile urlFile)
{
return $"{urlFile.name}.{urlFile.fileExtension}";
}
}
}
================================================
FILE: ModuleManager/FatalErrorHandler.cs
================================================
using System;
using UnityEngine;
namespace ModuleManager
{
public static class FatalErrorHandler
{
public static void HandleFatalError(string message)
{
try
{
PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f),
new Vector2(0.5f, 0.5f),
new MultiOptionDialog(
"ModuleManagerFatalError",
$"ModuleManager has encountered a fatal error and KSP needs to close.\n\n{message}\n\nPlease see KSP's log for addtional details",
"ModuleManager - Fatal Error",
HighLogic.UISkin,
new Rect(0.5f, 0.5f, 500f, 60f),
new DialogGUIFlexibleSpace(),
new DialogGUIHorizontalLayout(
new DialogGUIFlexibleSpace(),
new DialogGUIButton("Quit", Application.Quit, 140.0f, 30.0f, true),
new DialogGUIFlexibleSpace()
)
),
true,
HighLogic.UISkin);
}
catch(Exception ex)
{
Debug.LogError("Exception while trying to create the fatal exception dialog");
Debug.LogException(ex);
Application.Quit();
}
}
}
}
================================================
FILE: ModuleManager/FilePathRepository.cs
================================================
using System;
using System.IO;
namespace ModuleManager
{
internal static class FilePathRepository
{
internal static readonly string normalizedRootPath = Path.GetFullPath(KSPUtil.ApplicationRootPath);
internal static readonly string cachePath = Path.Combine(normalizedRootPath, "GameData", "ModuleManager.ConfigCache");
internal static readonly string techTreeFile = Path.Combine("GameData", "ModuleManager.TechTree");
internal static readonly string techTreePath = Path.Combine(normalizedRootPath, techTreeFile);
internal static readonly string physicsFile = Path.Combine("GameData", "ModuleManager.Physics");
internal static readonly string physicsPath = Path.Combine(normalizedRootPath, physicsFile);
internal static readonly string defaultPhysicsPath = Path.Combine(normalizedRootPath, "Physics.cfg");
internal static readonly string partDatabasePath = Path.Combine(normalizedRootPath, "PartDatabase.cfg");
internal static readonly string shaPath = Path.Combine(normalizedRootPath, "GameData", "ModuleManager.ConfigSHA");
internal static readonly string logsDirPath = Path.Combine(normalizedRootPath, "Logs", "ModuleManager");
internal static readonly string logPath = Path.Combine(logsDirPath, "ModuleManager.log");
internal static readonly string patchLogPath = Path.Combine(logsDirPath, "MMPatch.log");
}
}
================================================
FILE: ModuleManager/Fix16.cs
================================================
using System.Collections;
using System.Diagnostics.CodeAnalysis;
namespace ModuleManager
{
class Fix16 : LoadingSystem
{
[SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by Unity")]
private void Awake()
{
if (Instance != null)
{
DestroyImmediate(this);
return;
}
Instance = this;
DontDestroyOnLoad(gameObject);
}
private static Fix16 Instance { get; set; }
private bool ready;
private int count;
private int current;
private const int yieldStep = 20;
public override bool IsReady()
{
return ready;
}
public override string ProgressTitle()
{
return "Fix 1.6.0 " + current + "/" + count;
}
public override float ProgressFraction()
{
return (float) current / count;
}
public override void StartLoad()
{
ready = false;
count = PartLoader.LoadedPartsList.Count;
StartCoroutine(DoFix());
}
private IEnumerator DoFix()
{
int yieldCounter = 0;
for (current = 0; current < count; current++)
{
AvailablePart avp = PartLoader.LoadedPartsList[current];
if (avp.partPrefab.dragModel == Part.DragModel.CUBE && !avp.partPrefab.DragCubes.Procedural &&
!avp.partPrefab.DragCubes.None && avp.partPrefab.DragCubes.Cubes.Count == 0)
{
DragCubeSystem.Instance.LoadDragCubes(avp.partPrefab);
}
if (yieldCounter++ >= yieldStep)
{
yieldCounter = 0;
yield return null;
}
}
ready = true;
yield return null;
}
public override float LoadWeight()
{
return 0.1f;
}
}
}
================================================
FILE: ModuleManager/Logging/IBasicLogger.cs
================================================
using System;
namespace ModuleManager.Logging
{
// Stripped down version of UnityEngine.ILogger
public interface IBasicLogger
{
void Log(ILogMessage message);
}
}
================================================
FILE: ModuleManager/Logging/ILogMessage.cs
================================================
using System;
using UnityEngine;
namespace ModuleManager.Logging
{
public interface ILogMessage
{
LogType LogType { get; }
DateTime Timestamp { get; }
string Message { get; }
string ToLogString();
}
}
================================================
FILE: ModuleManager/Logging/LogMessage.cs
================================================
using System;
using UnityEngine;
namespace ModuleManager.Logging
{
public class LogMessage : ILogMessage
{
private const string DATETIME_FORMAT_STRING = "HH:mm:ss.fff";
public LogType LogType { get; }
public DateTime Timestamp { get; }
public string Message { get; }
public LogMessage(LogType logType, string message)
{
LogType = logType;
Timestamp = DateTime.Now;
Message = message ?? throw new ArgumentNullException(nameof(message));
}
public LogMessage(ILogMessage logMessage, string newMessage)
{
if (logMessage == null) throw new ArgumentNullException(nameof(logMessage));
LogType = logMessage.LogType;
Timestamp = logMessage.Timestamp;
Message = newMessage ?? throw new ArgumentNullException(nameof(newMessage));
}
public string ToLogString()
{
string prefix;
if (LogType == LogType.Log)
prefix = "LOG";
else if (LogType == LogType.Warning)
prefix = "WRN";
else if (LogType == LogType.Error)
prefix = "ERR";
else if (LogType == LogType.Assert)
prefix = "AST";
else if (LogType == LogType.Exception)
prefix = "EXC";
else
prefix = "???";
return $"[{prefix} {Timestamp.ToString(DATETIME_FORMAT_STRING)}] {Message}";
}
public override string ToString()
{
return $"[{GetType().FullName} LogType={LogType} Message={Message}]";
}
}
}
================================================
FILE: ModuleManager/Logging/LogSplitter.cs
================================================
using System;
namespace ModuleManager.Logging
{
public class LogSplitter : IBasicLogger
{
private readonly IBasicLogger logger1;
private readonly IBasicLogger logger2;
public LogSplitter(IBasicLogger logger1, IBasicLogger logger2)
{
this.logger1 = logger1 ?? throw new ArgumentNullException(nameof(logger1));
this.logger2 = logger2 ?? throw new ArgumentNullException(nameof(logger2));
}
public void Log(ILogMessage message)
{
if (message == null) throw new ArgumentNullException(nameof(message));
logger1.Log(message);
logger2.Log(message);
}
}
}
================================================
FILE: ModuleManager/Logging/PrefixLogger.cs
================================================
using System;
namespace ModuleManager.Logging
{
public class PrefixLogger : IBasicLogger
{
private readonly string prefix;
private readonly IBasicLogger logger;
public PrefixLogger(string prefix, IBasicLogger logger)
{
if (string.IsNullOrEmpty(prefix)) throw new ArgumentNullException(nameof(prefix));
this.prefix = $"[{prefix}] ";
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void Log(ILogMessage message)
{
if (message == null) throw new ArgumentNullException(nameof(message));
logger.Log(new LogMessage(message, prefix + message.Message));
}
}
}
================================================
FILE: ModuleManager/Logging/QueueLogRunner.cs
================================================
using System;
using System.IO;
using ModuleManager.Collections;
namespace ModuleManager.Logging
{
public class QueueLogRunner
{
private enum State
{
Initialized,
Running,
StopRequested,
Stopped,
}
private State state = State.Initialized;
private readonly IMessageQueue<ILogMessage> logQueue;
private readonly long timeToWaitForLogsMs;
public QueueLogRunner(IMessageQueue<ILogMessage> logQueue, long timeToWaitForLogsMs = 50)
{
this.logQueue = logQueue ?? throw new ArgumentNullException(nameof(logQueue));
if (timeToWaitForLogsMs < 0) throw new ArgumentException("must be non-negative", nameof(timeToWaitForLogsMs));
this.timeToWaitForLogsMs = timeToWaitForLogsMs;
}
public void RequestStop()
{
if (state == State.StopRequested || state == State.Stopped) return;
if (state != State.Running) throw new InvalidOperationException($"Cannot request stop from {state} state");
state = State.StopRequested;
}
public void Run(IBasicLogger logger)
{
if (state != State.Initialized) throw new InvalidOperationException($"Cannot run from {state} state");
if (logger == null) throw new ArgumentNullException(nameof(logger));
state = State.Running;
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
while (state == State.Running)
{
stopwatch.Start();
foreach (ILogMessage message in logQueue.TakeAll())
{
logger.Log(message);
}
long timeRemaining = timeToWaitForLogsMs - stopwatch.ElapsedMilliseconds;
if (timeRemaining > 0)
System.Threading.Thread.Sleep((int)timeRemaining);
stopwatch.Reset();
}
foreach (ILogMessage message in logQueue.TakeAll())
{
logger.Log(message);
}
state = State.Stopped;
}
}
}
================================================
FILE: ModuleManager/Logging/QueueLogger.cs
================================================
using System;
using ModuleManager.Collections;
namespace ModuleManager.Logging
{
public class QueueLogger : IBasicLogger
{
private readonly IMessageQueue<ILogMessage> queue;
public QueueLogger(IMessageQueue<ILogMessage> queue)
{
this.queue = queue ?? throw new ArgumentNullException(nameof(queue));
}
public void Log(ILogMessage message)
{
if (message == null) throw new ArgumentNullException(nameof(message));
queue.Add(message);
}
}
}
================================================
FILE: ModuleManager/Logging/StreamLogger.cs
================================================
using System;
using System.IO;
namespace ModuleManager.Logging
{
public sealed class StreamLogger : IBasicLogger, IDisposable
{
private readonly StreamWriter streamWriter;
private bool disposed = false;
public StreamLogger(Stream stream)
{
streamWriter = new StreamWriter(stream);
}
public void Log(ILogMessage message)
{
if (disposed) throw new InvalidOperationException("Object has already been disposed");
if (message == null) throw new ArgumentNullException(nameof(message));
streamWriter.WriteLine(message.ToLogString());
}
public void Dispose()
{
// Flushes and closes the StreamWriter and the underlying stream
streamWriter.Close();
disposed = true;
}
}
}
================================================
FILE: ModuleManager/Logging/UnityLogger.cs
================================================
using System;
using UnityEngine;
namespace ModuleManager.Logging
{
public class UnityLogger : IBasicLogger
{
private readonly ILogger logger;
public UnityLogger(ILogger logger)
{
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void Log(ILogMessage message)
{
if (message == null) throw new ArgumentNullException(nameof(message));
logger.Log(message.LogType, message.Message);
}
}
}
================================================
FILE: ModuleManager/MMPatchLoader.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using ModuleManager.Collections;
using ModuleManager.Logging;
using ModuleManager.Extensions;
using ModuleManager.Threading;
using ModuleManager.Tags;
using ModuleManager.Patches;
using ModuleManager.Progress;
using NodeStack = ModuleManager.Collections.ImmutableStack<ConfigNode>;
using static ModuleManager.FilePathRepository;
namespace ModuleManager
{
public class MMPatchLoader
{
private const string PHYSICS_NODE_NAME = "PHYSICSGLOBALS";
private const string TECH_TREE_NODE_NAME = "TechTree";
public string status = "";
public string errors = "";
public static bool keepPartDB = false;
private static readonly KeyValueCache<string, Regex> regexCache = new KeyValueCache<string, Regex>();
private string configSha;
private readonly Dictionary<string, string> filesSha = new Dictionary<string, string>();
private const int STATUS_UPDATE_INVERVAL_MS = 33;
private readonly IEnumerable<ModListGenerator.ModAddedByAssembly> modsAddedByAssemblies;
private readonly IBasicLogger logger;
public static void AddPostPatchCallback(ModuleManagerPostPatchCallback callback)
{
PostPatchLoader.AddPostPatchCallback(callback);
}
public MMPatchLoader(IEnumerable<ModListGenerator.ModAddedByAssembly> modsAddedByAssemblies, IBasicLogger logger)
{
this.modsAddedByAssemblies = modsAddedByAssemblies ?? throw new ArgumentNullException(nameof(modsAddedByAssemblies));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public IEnumerable<IProtoUrlConfig> Run()
{
Stopwatch patchSw = new Stopwatch();
patchSw.Start();
status = "Checking Cache";
logger.Info(status);
bool useCache = false;
try
{
useCache = IsCacheUpToDate();
}
catch (Exception ex)
{
logger.Exception("Exception in IsCacheUpToDate", ex);
}
#if DEBUG
//useCache = false;
#endif
IEnumerable<IProtoUrlConfig> databaseConfigs = null;
if (!useCache)
{
if (!Directory.Exists(logsDirPath)) Directory.CreateDirectory(logsDirPath);
MessageQueue<ILogMessage> patchLogQueue = new MessageQueue<ILogMessage>();
QueueLogRunner logRunner = new QueueLogRunner(patchLogQueue);
ITaskStatus loggingThreadStatus = BackgroundTask.Start(delegate
{
using StreamLogger streamLogger = new StreamLogger(new FileStream(patchLogPath, FileMode.Create));
streamLogger.Info("Log started at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
logRunner.Run(streamLogger);
streamLogger.Info("Done!");
});
IBasicLogger patchLogger = new LogSplitter(logger, new QueueLogger(patchLogQueue));
IPatchProgress progress = new PatchProgress(patchLogger);
status = "Pre patch init";
patchLogger.Info(status);
IEnumerable<string> mods = ModListGenerator.GenerateModList(modsAddedByAssemblies, progress, patchLogger);
// If we don't use the cache then it is best to clean the PartDatabase.cfg
if (!keepPartDB && File.Exists(partDatabasePath))
File.Delete(partDatabasePath);
LoadPhysicsConfig();
#region Sorting Patches
status = "Extracting patches";
patchLogger.Info(status);
UrlDir gameData = GameDatabase.Instance.root.children.First(dir => dir.type == UrlDir.DirectoryType.GameData && dir.name == "");
INeedsChecker needsChecker = new NeedsChecker(mods, gameData, progress, patchLogger);
ITagListParser tagListParser = new TagListParser(progress);
IProtoPatchBuilder protoPatchBuilder = new ProtoPatchBuilder(progress);
IPatchCompiler patchCompiler = new PatchCompiler();
PatchExtractor extractor = new PatchExtractor(progress, patchLogger, needsChecker, tagListParser, protoPatchBuilder, patchCompiler);
// Have to convert to an array because we will be removing patches
IEnumerable<IPatch> extractedPatches =
GameDatabase.Instance.root.AllConfigs.Select(urlConfig => extractor.ExtractPatch(urlConfig)).Where(patch => patch != null);
PatchList patchList = new PatchList(mods, extractedPatches, progress);
#endregion
#region Applying patches
status = "Applying patches";
patchLogger.Info(status);
IPass currentPass = null;
progress.OnPassStarted.Add(delegate (IPass pass)
{
currentPass = pass;
StatusUpdate(progress, currentPass.Name);
});
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
progress.OnPatchApplied.Add(delegate
{
long timeRemaining = STATUS_UPDATE_INVERVAL_MS - stopwatch.ElapsedMilliseconds;
if (timeRemaining < 0)
{
StatusUpdate(progress, currentPass.Name);
stopwatch.Reset();
stopwatch.Start();
}
});
PatchApplier applier = new PatchApplier(progress, patchLogger);
databaseConfigs = applier.ApplyPatches(patchList);
stopwatch.Stop();
StatusUpdate(progress);
patchLogger.Info("Done patching");
#endregion Applying patches
#region Saving Cache
foreach (KeyValuePair<string, int> item in progress.Counter.warningFiles)
{
patchLogger.Warning(item.Value + " warning" + (item.Value > 1 ? "s" : "") + " related to GameData/" + item.Key);
}
if (progress.Counter.errors > 0 || progress.Counter.exceptions > 0)
{
foreach (KeyValuePair<string, int> item in progress.Counter.errorFiles)
{
errors += item.Value + " error" + (item.Value > 1 ? "s" : "") + " related to GameData/" + item.Key
+ "\n";
}
patchLogger.Warning("Errors in patch prevents the creation of the cache");
try
{
if (File.Exists(cachePath))
File.Delete(cachePath);
if (File.Exists(shaPath))
File.Delete(shaPath);
}
catch (Exception e)
{
patchLogger.Exception("Exception while deleting stale cache ", e);
}
}
else
{
status = "Saving Cache";
patchLogger.Info(status);
CreateCache(databaseConfigs, progress.Counter.patchedNodes);
}
StatusUpdate(progress);
#endregion Saving Cache
SaveModdedTechTree(databaseConfigs);
SaveModdedPhysics(databaseConfigs);
logRunner.RequestStop();
while (loggingThreadStatus.IsRunning)
{
System.Threading.Thread.Sleep(100);
}
if (loggingThreadStatus.IsExitedWithError)
{
logger.Error("The patching thread threw an exception");
throw loggingThreadStatus.Exception;
}
}
else
{
status = "Loading from Cache";
logger.Info(status);
databaseConfigs = LoadCache();
if (File.Exists(patchLogPath))
{
logger.Info("Dumping patch log");
logger.Info("\n#### BEGIN PATCH LOG ####\n\n\n" + File.ReadAllText(patchLogPath) + "\n\n\n#### END PATCH LOG ####");
}
else
{
logger.Error("Patch log does not exist: " + patchLogPath);
}
}
if (KSP.Localization.Localizer.Instance != null)
KSP.Localization.Localizer.SwitchToLanguage(KSP.Localization.Localizer.CurrentLanguage);
logger.Info(status + "\n" + errors);
patchSw.Stop();
logger.Info("Ran in " + ((float)patchSw.ElapsedMilliseconds / 1000).ToString("F3") + "s");
return databaseConfigs;
}
private void LoadPhysicsConfig()
{
logger.Info("Loading Physics.cfg");
UrlDir gameDataDir = GameDatabase.Instance.root.AllDirectories.First(d => d.path.EndsWith("GameData") && d.name == "" && d.url == "");
// need to use a file with a cfg extension to get the right fileType or you can't AddConfig on it
UrlDir.UrlFile physicsUrlFile = new UrlDir.UrlFile(gameDataDir, new FileInfo(defaultPhysicsPath));
// Since it loaded the default config badly (sub node only) we clear it first
physicsUrlFile.configs.Clear();
// And reload it properly
ConfigNode physicsContent = ConfigNode.Load(defaultPhysicsPath);
physicsContent.name = PHYSICS_NODE_NAME;
physicsUrlFile.AddConfig(physicsContent);
gameDataDir.files.Add(physicsUrlFile);
}
private void SaveModdedPhysics(IEnumerable<IProtoUrlConfig> databaseConfigs)
{
IEnumerable<IProtoUrlConfig> configs = databaseConfigs.Where(config => config.NodeType == PHYSICS_NODE_NAME);
int count = configs.Count();
if (count == 0)
{
logger.Info($"No {PHYSICS_NODE_NAME} node found. No custom Physics config will be saved");
return;
}
if (count > 1)
{
logger.Info($"{count} {PHYSICS_NODE_NAME} nodes found. A patch may be wrong. Using the first one");
}
configs.First().Node.Save(physicsPath);
}
private bool IsCacheUpToDate()
{
Stopwatch sw = new Stopwatch();
sw.Start();
using System.Security.Cryptography.SHA256 sha = System.Security.Cryptography.SHA256.Create();
using System.Security.Cryptography.SHA256 filesha = System.Security.Cryptography.SHA256.Create();
UrlDir.UrlFile[] files = GameDatabase.Instance.root.AllConfigFiles.ToArray();
filesSha.Clear();
for (int i = 0; i < files.Length; i++)
{
string url = files[i].GetUrlWithExtension();
// Hash the file path so the checksum change if files are moved
byte[] pathBytes = Encoding.UTF8.GetBytes(url);
sha.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);
// hash the file content
byte[] contentBytes = File.ReadAllBytes(files[i].fullPath);
sha.TransformBlock(contentBytes, 0, contentBytes.Length, contentBytes, 0);
filesha.ComputeHash(contentBytes);
if (!filesSha.ContainsKey(url))
{
filesSha.Add(url, BitConverter.ToString(filesha.Hash));
}
else
{
logger.Warning("Duplicate fileSha key. This should not append. The key is " + url);
}
}
// Hash the mods dll path so the checksum change if dlls are moved or removed (impact NEEDS)
foreach (AssemblyLoader.LoadedAssembly dll in AssemblyLoader.loadedAssemblies)
{
string path = dll.url + "/" + dll.name;
byte[] pathBytes = Encoding.UTF8.GetBytes(path);
sha.TransformBlock(pathBytes, 0, pathBytes.Length, pathBytes, 0);
}
foreach (ModListGenerator.ModAddedByAssembly mod in modsAddedByAssemblies)
{
byte[] modBytes = Encoding.UTF8.GetBytes(mod.modName);
sha.TransformBlock(modBytes, 0, modBytes.Length, modBytes, 0);
}
byte[] godsFinalMessageToHisCreation = Encoding.UTF8.GetBytes("We apologize for the inconvenience.");
sha.TransformFinalBlock(godsFinalMessageToHisCreation, 0, godsFinalMessageToHisCreation.Length);
configSha = BitConverter.ToString(sha.Hash);
sha.Clear();
filesha.Clear();
sw.Stop();
logger.Info("SHA generated in " + ((float)sw.ElapsedMilliseconds / 1000).ToString("F3") + "s");
logger.Info(" SHA = " + configSha);
bool useCache = false;
if (File.Exists(shaPath))
{
ConfigNode shaConfigNode = ConfigNode.Load(shaPath);
if (shaConfigNode != null && shaConfigNode.HasValue("SHA") && shaConfigNode.HasValue("version") && shaConfigNode.HasValue("KSPVersion"))
{
string storedSHA = shaConfigNode.GetValue("SHA");
string version = shaConfigNode.GetValue("version");
string kspVersion = shaConfigNode.GetValue("KSPVersion");
ConfigNode filesShaNode = shaConfigNode.GetNode("FilesSHA");
useCache = CheckFilesChange(files, filesShaNode);
useCache = useCache && storedSHA.Equals(configSha);
useCache = useCache && version.Equals(Assembly.GetExecutingAssembly().GetName().Version.ToString());
useCache = useCache && kspVersion.Equals(Versioning.version_major + "." + Versioning.version_minor + "." + Versioning.Revision + "." + Versioning.BuildID);
useCache = useCache && File.Exists(cachePath);
useCache = useCache && File.Exists(physicsPath);
useCache = useCache && File.Exists(techTreePath);
logger.Info("Cache SHA = " + storedSHA);
logger.Info("useCache = " + useCache);
}
}
return useCache;
}
private bool CheckFilesChange(UrlDir.UrlFile[] files, ConfigNode shaConfigNode)
{
bool noChange = true;
StringBuilder changes = new StringBuilder();
changes.Append("Changes :\n");
for (int i = 0; i < files.Length; i++)
{
string url = files[i].GetUrlWithExtension();
ConfigNode fileNode = GetFileNode(shaConfigNode, url);
string fileSha = fileNode?.GetValue("SHA");
if (fileNode == null)
continue;
if (fileSha == null || filesSha[url] != fileSha)
{
changes.Append("Changed : " + fileNode.GetValue("filename") + ".cfg\n");
noChange = false;
}
}
for (int i = 0; i < files.Length; i++)
{
string url = files[i].GetUrlWithExtension();
ConfigNode fileNode = GetFileNode(shaConfigNode, url);
if (fileNode == null)
{
changes.Append("Added : " + url + "\n");
noChange = false;
}
shaConfigNode.RemoveNode(fileNode);
}
foreach (ConfigNode fileNode in shaConfigNode.GetNodes())
{
changes.Append("Deleted : " + fileNode.GetValue("filename") + "\n");
noChange = false;
}
if (!noChange)
logger.Info(changes.ToString());
return noChange;
}
private ConfigNode GetFileNode(ConfigNode shaConfigNode, string filename)
{
for (int i = 0; i < shaConfigNode.nodes.Count; i++)
{
ConfigNode file = shaConfigNode.nodes[i];
if (file.name == "FILE" && file.GetValue("filename") == filename)
return file;
}
return null;
}
private void CreateCache(IEnumerable<IProtoUrlConfig> databaseConfigs, int patchedNodeCount)
{
ConfigNode shaConfigNode = new ConfigNode();
shaConfigNode.AddValue("SHA", configSha);
shaConfigNode.AddValue("version", Assembly.GetExecutingAssembly().GetName().Version.ToString());
shaConfigNode.AddValue("KSPVersion", Versioning.version_major + "." + Versioning.version_minor + "." + Versioning.Revision + "." + Versioning.BuildID);
ConfigNode filesSHANode = shaConfigNode.AddNode("FilesSHA");
ConfigNode cache = new ConfigNode();
cache.AddValue("patchedNodeCount", patchedNodeCount.ToString());
foreach (IProtoUrlConfig urlConfig in databaseConfigs)
{
ConfigNode node = cache.AddNode("UrlConfig");
node.AddValue("parentUrl", urlConfig.UrlFile.GetUrlWithExtension());
ConfigNode urlNode = urlConfig.Node.DeepCopy();
urlNode.EscapeValuesRecursive();
node.AddNode(urlNode);
}
foreach (var file in GameDatabase.Instance.root.AllConfigFiles)
{
string url = file.GetUrlWithExtension();
// "/Physics" is the node we created manually to loads the PHYSIC config
if (file.url != "/Physics" && filesSha.ContainsKey(url))
{
ConfigNode shaNode = filesSHANode.AddNode("FILE");
shaNode.AddValue("filename", url);
shaNode.AddValue("SHA", filesSha[url]);
filesSha.Remove(url);
}
}
logger.Info("Saving cache");
try
{
shaConfigNode.Save(shaPath);
}
catch (Exception e)
{
logger.Exception("Exception while saving the sha", e);
}
try
{
cache.Save(cachePath);
return;
}
catch (NullReferenceException e)
{
logger.Exception("NullReferenceException while saving the cache", e);
}
catch (Exception e)
{
logger.Exception("Exception while saving the cache", e);
}
try
{
logger.Error("An error occured while creating the cache. Deleting the cache files to avoid keeping a bad cache");
if (File.Exists(cachePath))
File.Delete(cachePath);
if (File.Exists(shaPath))
File.Delete(shaPath);
}
catch (Exception e)
{
logger.Exception("Exception while deleting the cache", e);
}
}
private void SaveModdedTechTree(IEnumerable<IProtoUrlConfig> databaseConfigs)
{
IEnumerable<IProtoUrlConfig> configs = databaseConfigs.Where(config => config.NodeType == TECH_TREE_NODE_NAME);
int count = configs.Count();
if (count == 0)
{
logger.Info($"No {TECH_TREE_NODE_NAME} node found. No custom {TECH_TREE_NODE_NAME} will be saved");
return;
}
if (count > 1)
{
logger.Info($"{count} {TECH_TREE_NODE_NAME} nodes found. A patch may be wrong. Using the first one");
}
ConfigNode techNode = new ConfigNode(TECH_TREE_NODE_NAME);
techNode.AddNode(configs.First().Node);
techNode.Save(techTreePath);
}
private IEnumerable<IProtoUrlConfig> LoadCache()
{
ConfigNode cache = ConfigNode.Load(cachePath);
if (cache.HasValue("patchedNodeCount") && int.TryParse(cache.GetValue("patchedNodeCount"), out int patchedNodeCount))
status = "ModuleManager: " + patchedNodeCount + " patch" + (patchedNodeCount != 1 ? "es" : "") + " loaded from cache";
// Create the fake file where we load the physic config cache
UrlDir gameDataDir = GameDatabase.Instance.root.AllDirectories.First(d => d.path.EndsWith("GameData") && d.name == "" && d.url == "");
// need to use a file with a cfg extension to get the right fileType or you can't AddConfig on it
UrlDir.UrlFile physicsUrlFile = new UrlDir.UrlFile(gameDataDir, new FileInfo(defaultPhysicsPath));
gameDataDir.files.Add(physicsUrlFile);
List<IProtoUrlConfig> databaseConfigs = new List<IProtoUrlConfig>(cache.nodes.Count);
foreach (ConfigNode node in cache.nodes)
{
string parentUrl = node.GetValue("parentUrl");
UrlDir.UrlFile parent = gameDataDir.Find(parentUrl);
if (parent != null)
{
node.nodes[0].UnescapeValuesRecursive();
databaseConfigs.Add(new ProtoUrlConfig(parent, node.nodes[0]));
}
else
{
logger.Warning("Parent null for " + parentUrl);
}
}
logger.Info("Cache Loaded");
return databaseConfigs;
}
private void StatusUpdate(IPatchProgress progress, string activity = null)
{
status = "ModuleManager: " + progress.Counter.patchedNodes + " patch" + (progress.Counter.patchedNodes != 1 ? "es" : "") + " applied";
if (progress.ProgressFraction < 1f - float.Epsilon)
status += " (" + progress.ProgressFraction.ToString("P0") + ")";
if (activity != null)
status += "\n" + activity;
if (progress.Counter.warnings > 0)
status += ", found <color=yellow>" + progress.Counter.warnings + " warning" + (progress.Counter.warnings != 1 ? "s" : "") + "</color>";
if (progress.Counter.errors > 0)
status += ", found <color=orange>" + progress.Counter.errors + " error" + (progress.Counter.errors != 1 ? "s" : "") + "</color>";
if (progress.Counter.exceptions > 0)
status += ", encountered <color=red>" + progress.Counter.exceptions + " exception" + (progress.Counter.exceptions != 1 ? "s" : "") + "</color>";
}
#region Applying Patches
// Name is group 1, index is group 2, vector related filed is group 3, vector separator is group 4, operator is group 5
private static readonly Regex parseValue = new Regex(@"([\w\&\-\.\?\*\#+/^!\(\) ]+(?:,[^*\d][\w\&\-\.\?\*\(\) ]*)*)(?:,(-?[0-9\*]+))?(?:\[((?:[0-9\*]+)+)(?:,(.))?\])?");
// ModifyNode applies the ConfigNode mod as a 'patch' to ConfigNode original, then returns the patched ConfigNode.
// it uses FindConfigNodeIn(src, nodeType, nodeName, nodeTag) to recurse.
public static ConfigNode ModifyNode(NodeStack original, ConfigNode mod, PatchContext context)
{
ConfigNode newNode = original.value.DeepCopy();
NodeStack nodeStack = original.ReplaceValue(newNode);
#region Values
#if LOGSPAM
string vals = "[ModuleManager] modding values";
#endif
foreach (ConfigNode.Value modVal in mod.values)
{
#if LOGSPAM
vals += "\n " + modVal.name + "= " + modVal.value;
#endif
Command cmd = CommandParser.Parse(modVal.name, out string valName);
Operator op;
if (valName.Length > 2 && valName[valName.Length - 2] == ',')
op = Operator.Assign;
else
op = OperatorParser.Parse(valName, out valName);
if (cmd == Command.Special)
{
ConfigNode.Value val = RecurseVariableSearch(valName, nodeStack.Push(mod), context);
if (val == null)
{
context.progress.Error(context.patchUrl, "Error - Cannot find value assigning command: " + valName);
continue;
}
if (op != Operator.Assign)
{
if (double.TryParse(modVal.value, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out double s)
&& double.TryParse(val.value, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out double os))
{
switch (op)
{
case Operator.Multiply:
val.value = (os * s).ToString(CultureInfo.InvariantCulture);
break;
case Operator.Divide:
val.value = (os / s).ToString(CultureInfo.InvariantCulture);
break;
case Operator.Add:
val.value = (os + s).ToString(CultureInfo.InvariantCulture);
break;
case Operator.Subtract:
val.value = (os - s).ToString(CultureInfo.InvariantCulture);
break;
case Operator.Exponentiate:
val.value = Math.Pow(os, s).ToString(CultureInfo.InvariantCulture);
break;
}
}
}
else
{
val.value = modVal.value;
}
continue;
}
Match match = parseValue.Match(valName);
if (!match.Success)
{
context.progress.Error(context.patchUrl, "Error - Cannot parse value modifying command: " + valName);
continue;
}
// Get the bits and pieces from the regexp
valName = match.Groups[1].Value;
// Get a position for editing a vector
int position = 0;
bool isPosStar = false;
if (match.Groups[3].Success)
{
if (match.Groups[3].Value == "*")
isPosStar = true;
else if (!int.TryParse(match.Groups[3].Value, out position))
{
context.progress.Error(context.patchUrl, "Error - Unable to parse number as number. Very odd.");
continue;
}
}
char seperator = ',';
if (match.Groups[4].Success)
{
seperator = match.Groups[4].Value[0];
}
// In this case insert the value at position index (with the same node names)
int index = 0;
bool isStar = false;
if (match.Groups[2].Success)
{
if (match.Groups[2].Value == "*")
isStar = true;
// can have "node,n *" (for *= ect)
else if (!int.TryParse(match.Groups[2].Value, out index))
{
context.progress.Error(context.patchUrl, "Error - Unable to parse number as number. Very odd.");
continue;
}
}
int valCount = 0;
for (int i=0; i<newNode.CountValues; i++)
if (newNode.values[i].name == valName)
valCount++;
string varValue;
switch (cmd)
{
case Command.Insert:
if (match.Groups[5].Success)
{
context.progress.Error(context.patchUrl, "Error - Cannot use operators with insert value: " + mod.name);
}
else
{
// Insert at the end by default
varValue = ProcessVariableSearch(modVal.value, nodeStack, context);
if (varValue != null)
InsertValue(newNode, match.Groups[2].Success ? index : int.MaxValue, valName, varValue);
else
context.progress.Error(context.patchUrl, "Error - Cannot parse variable search when inserting new key " + valName + " = " +
modVal.value);
}
break;
case Command.Replace:
if (match.Groups[2].Success || match.Groups[5].Success || valName.Contains('*')
|| valName.Contains('?'))
{
if (match.Groups[2].Success)
context.progress.Error(context.patchUrl, "Error - Cannot use index with replace (%) value: " + mod.name);
if (match.Groups[5].Success)
context.progress.Error(context.patchUrl, "Error - Cannot use operators with replace (%) value: " + mod.name);
if (valName.Contains('*') || valName.Contains('?'))
context.progress.Error(context.patchUrl, "Error - Cannot use wildcards (* or ?) with replace (%) value: " + mod.name);
}
else
{
varValue = ProcessVariableSearch(modVal.value, nodeStack, context);
if (varValue != null)
{
newNode.RemoveValues(valName);
newNode.AddValueSafe(valName, varValue);
}
else
{
context.progress.Error(context.patchUrl, "Error - Cannot parse variable search when replacing (%) key " + valName + " = " +
modVal.value);
}
}
break;
case Command.Edit:
case Command.Copy:
// Format is @key = value or @key *= value or @key += value or @key -= value
// or @key,index = value or @key,index *= value or @key,index += value or @key,index -= value
while (index < valCount)
{
varValue = ProcessVariableSearch(modVal.value, nodeStack, context);
if (varValue != null)
{
string value = FindAndReplaceValue(
mod,
ref valName,
varValue, newNode,
op,
index,
out ConfigNode.Value origVal,
context,
match.Groups[3].Success,
position,
isPosStar,
seperator
);
if (value != null)
{
#if LOGSPAM
if (origVal.value != value)
vals += ": " + origVal.value + " -> " + value;
#endif
if (cmd != Command.Copy)
origVal.value = value;
else
newNode.AddValueSafe(valName, value);
}
}
else
{
context.progress.Error(context.patchUrl, "Error - Cannot parse variable search when editing key " + valName + " = " + modVal.value);
}
if (isStar) index++;
else break;
}
break;
case Command.Delete:
if (match.Groups[5].Success)
{
context.progress.Error(context.patchUrl, "Error - Cannot use operators with delete (- or !) value: " + mod.name);
}
else if (match.Groups[2].Success)
{
while (index < valCount)
{
// If there is an index, use it.
ConfigNode.Value v = FindValueIn(newNode, valName, index);
if (v != null)
newNode.values.Remove(v);
if (isStar) index++;
else break;
}
}
else if (valName.Contains('*') || valName.Contains('?'))
{
// Delete all matching wildcard
ConfigNode.Value last = null;
while (true)
{
ConfigNode.Value v = FindValueIn(newNode, valName, index++);
if (v == last)
break;
last = v;
newNode.values.Remove(v);
}
}
else
{
// Default is to delete ALL values that match. (backwards compatibility)
newNode.RemoveValues(valName);
}
break;
case Command.Rename:
if (nodeStack.IsRoot)
{
context.progress.Error(context.patchUrl, "Error - Renaming nodes does not work on top nodes");
break;
}
newNode.name = modVal.value;
break;
case Command.Create:
if (match.Groups[2].Success || match.Groups[5].Success || valName.Contains('*')
|| valName.Contains('?'))
{
if (match.Groups[2].Success)
context.progress.Error(context.patchUrl, "Error - Cannot use index with create (&) value: " + mod.name);
if (match.Groups[5].Success)
context.progress.Error(context.patchUrl, "Error - Cannot use operators with create (&) value: " + mod.name);
if (valName.Contains('*') || valName.Contains('?'))
context.progress.Error(context.patchUrl, "Error - Cannot use wildcards (* or ?) with create (&) value: " + mod.name);
}
else
{
varValue = ProcessVariableSearch(modVal.value, nodeStack, context);
if (varValue != null)
{
if (!newNode.HasValue(valName))
newNode.AddValueSafe(valName, varValue);
}
else
{
context.progress.Error(context.patchUrl, "Error - Cannot parse variable search when replacing (&) key " + valName + " = " +
modVal.value);
}
}
break;
}
}
#if LOGSPAM
log(vals);
#endif
#endregion Values
#region Nodes
foreach (ConfigNode subMod in mod.nodes)
{
subMod.name = subMod.name.RemoveWS();
if (!subMod.name.IsBracketBalanced())
{
context.progress.Error(context.patchUrl,
"Error - Skipping a patch subnode with unbalanced square brackets or a space (replace them with a '?') in "
+ mod.name + " : \n" + subMod.name + "\n");
continue;
}
string subName = subMod.name;
Command command = CommandParser.Parse(subName, out string tmp);
if (command == Command.Insert)
{
ConfigNode newSubMod = new ConfigNode(subMod.name);
newSubMod = ModifyNode(nodeStack.Push(newSubMod), subMod, context);
subName = newSubMod.name;
if (subName.Contains(",") && int.TryParse(subName.Split(',')[1], out int index))
{
// In this case insert the node at position index (with the same node names)
newSubMod.name = subName.Split(',')[0];
InsertNode(newNode, newSubMod, index);
}
else
{
newNode.AddNode(newSubMod);
}
}
else if (command == Command.Paste)
{
//int start = subName.IndexOf('[');
//int end = subName.LastIndexOf(']');
//if (start == -1 || end == -1 || end - start < 1)
//{
// log("Pasting a node require a [path] to the node to paste" + mod.name + " : \n" + subMod.name + "\n");
// errorCount++;
// continue;
//}
//string newName = subName.Substring(0, start);
//string path = subName.Substring(start + 1, end - start - 1);
ConfigNode toPaste = RecurseNodeSearch(subName.Substring(1), nodeStack, context);
if (toPaste == null)
{
context.progress.Error(context.patchUrl, "Error - Can not find the node to paste in " + mod.name + " : " + subMod.name + "\n");
continue;
}
ConfigNode newSubMod = new ConfigNode(toPaste.name);
newSubMod = ModifyNode(nodeStack.Push(newSubMod), toPaste, context);
if (subName.LastIndexOf(',') > 0 && int.TryParse(subName.Substring(subName.LastIndexOf(',') + 1), out int index))
{
// In this case insert the node at position index
InsertNode(newNode, newSubMod, index);
}
else
newNode.AddNode(newSubMod);
}
else
{
string constraints = "";
string tag = "";
string nodeType, nodeName;
int index = 0;
#if LOGSPAM
string msg = "";
#endif
List<ConfigNode> subNodes = new List<ConfigNode>();
// three ways to specify:
// NODE,n will match the nth node (NODE is the same as NODE,0)
// NODE,* will match ALL nodes
// NODE:HAS[condition] will match ALL nodes with condition
if (subName.Contains(":HAS[", out int hasStart))
{
constraints = subName.Substring(hasStart + 5, subName.LastIndexOf(']') - hasStart - 5);
subName = subName.Substring(0, hasStart);
}
if (subName.Contains(","))
{
tag = subName.Split(',')[1];
subName = subName.Split(',')[0];
int.TryParse(tag, out index);
}
if (subName.Contains("["))
{
// format @NODETYPE[Name] {...}
// or @NODETYPE[Name, index] {...}
nodeType = subName.Substring(1).Split('[')[0];
nodeName = subName.Split('[')[1].Replace("]", "");
}
else
{
// format @NODETYPE {...} or ! instead of @
nodeType = subName.Substring(1);
nodeName = null;
}
if (tag == "*" || constraints.Length > 0)
{
// get ALL nodes
if (command != Command.Replace)
{
ConfigNode n, last = null;
while (true)
{
n = FindConfigNodeIn(newNode, nodeType, nodeName, index++);
if (n == last || n == null)
break;
if (CheckConstraints(n, constraints))
subNodes.Add(n);
last = n;
}
}
#if LOGSPAM
else
msg += " cannot wildcard a % node: " + subMod.name + "\n";
#endif
}
else
{
// just get one node
ConfigNode n = FindConfigNodeIn(newNode, nodeType, nodeName, index);
if (n != null)
subNodes.Add(n);
}
if (command == Command.Replace)
{
// if the original exists modify it
if (subNodes.Count > 0)
{
#if LOGSPAM
msg += " Applying subnode " + subMod.name + "\n";
#endif
ConfigNode newSubNode = ModifyNode(nodeStack.Push(subNodes[0]), subMod, context);
subNodes[0].ShallowCopyFrom(newSubNode);
subNodes[0].name = newSubNode.name;
}
else
{
// if not add the mod node without the % in its name
#if LOGSPAM
msg += " Adding subnode " + subMod.name + "\n";
#endif
ConfigNode copy = new ConfigNode(nodeType);
if (nodeName != null)
copy.AddValueSafe("name", nodeName);
ConfigNode newSubNode = ModifyNode(nodeStack.Push(copy), subMod, context);
newNode.nodes.Add(newSubNode);
}
}
else if (command == Command.Create)
{
if (subNodes.Count == 0)
{
#if LOGSPAM
msg += " Adding subnode " + subMod.name + "\n";
#endif
ConfigNode copy = new ConfigNode(nodeType);
if (nodeName != null)
copy.AddValueSafe("name", nodeName);
ConfigNode newSubNode = ModifyNode(nodeStack.Push(copy), subMod, context);
newNode.nodes.Add(newSubNode);
}
}
else
{
// find each original subnode to modify, modify it and add the modified.
#if LOGSPAM
if (subNodes.Count == 0) // no nodes to modify!
msg += " Could not find node(s) to modify: " + subMod.name + "\n";
#endif
foreach (ConfigNode subNode in subNodes)
{
#if LOGSPAM
msg += " Applying subnode " + subMod.name + "\n";
#endif
ConfigNode newSubNode;
switch (command)
{
case Command.Edit:
// Edit in place
newSubNode = ModifyNode(nodeStack.Push(subNode), subMod, context);
subNode.ShallowCopyFrom(newSubNode);
subNode.name = newSubNode.name;
break;
case Command.Delete:
// Delete the node
newNode.nodes.Remove(subNode);
break;
case Command.Copy:
// Copy the node
newSubNode = ModifyNode(nodeStack.Push(subNode), subMod, context);
newNode.nodes.Add(newSubNode);
break;
}
}
}
#if LOGSPAM
print(msg);
#endif
}
}
#endregion Nodes
return newNode;
}
// Search for a ConfigNode by a path alike string
private static ConfigNode RecurseNodeSearch(string path, NodeStack nodeStack, PatchContext context)
{
//log("Path : \"" + path + "\"");
if (path[0] == '/')
{
return RecurseNodeSearch(path.Substring(1), nodeStack.Root, context);
}
int nextSep = path.IndexOf('/');
bool root = (path[0] == '@');
int shift = root ? 1 : 0;
string subName = (nextSep != -1) ? path.Substring(shift, nextSep - shift) : path.Substring(shift);
string nodeType, nodeName;
string constraint = "";
int index = 0;
if (subName.Contains(":HAS[", out int hasStart))
{
constraint = subName.Substring(hasStart + 5, subName.LastIndexOf(']') - hasStart - 5);
subName = subName.Substring(0, hasStart);
}
else if (subName.Contains(","))
{
string tag = subName.Split(',')[1];
subName = subName.Split(',')[0];
int.TryParse(tag, out index);
}
if (subName.Contains("["))
{
// NODETYPE[Name]
nodeType = subName.Split('[')[0];
nodeName = subName.Split('[')[1].Replace("]", "");
}
else
{
// NODETYPE
nodeType = subName;
nodeName = null;
}
// ../XXXXX
if (path.StartsWith("../"))
{
if (nodeStack.IsRoot)
return null;
return RecurseNodeSearch(path.Substring(3), nodeStack.Pop(), context);
}
//log("nextSep : \"" + nextSep + " \" root : \"" + root + " \" nodeType : \"" + nodeType + "\" nodeName : \"" + nodeName + "\"");
// @XXXXX
if (root)
{
bool foundNodeType = false;
foreach (IProtoUrlConfig urlConfig in context.databaseConfigs)
{
ConfigNode node = urlConfig.Node;
if (node.name != nodeType) continue;
foundNodeType = true;
if (nodeName == null || (node.GetValue("name") is string testNodeName && WildcardMatch(testNodeName, nodeName)))
{
nodeStack = new NodeStack(node);
break;
}
}
if (!foundNodeType) context.logger.Warning("Can't find nodeType:" + nodeType);
if (nodeStack == null) return null;
}
else
{
if (constraint.Length > 0)
{
// get the first one matching
ConfigNode last = null;
while (true)
{
ConfigNode n = FindConfigNodeIn(nodeStack.value, nodeType, nodeName, index++);
if (n == last || n == null)
{
nodeStack = null;
break;
}
if (CheckConstraints(n, constraint))
{
nodeStack = nodeStack.Push(n);
break;
}
last = n;
}
}
else
{
// just get one node
nodeStack = nodeStack.Push(FindConfigNodeIn(nodeStack.value, nodeType, nodeName, index));
}
}
// XXXXXX/
if (nextSep > 0 && nodeStack != null)
{
path = path.Substring(nextSep + 1);
//log("NewPath : \"" + path + "\"");
return RecurseNodeSearch(path, nodeStack, context);
}
return nodeStack.value;
}
// KeyName is group 1, index is group 2, value index is group 3, value separator is group 4
private static readonly Regex parseVarKey = new Regex(@"([\w\&\-\.]+)(?:,((?:[0-9]+)+))?(?:\[((?:[0-9]+)+)(?:,(.))?\])?");
// Search for a value by a path alike string
private static ConfigNode.Value RecurseVariableSearch(string path, NodeStack nodeStack, PatchContext context)
{
//log("path:" + path);
if (path[0] == '/')
return RecurseVariableSearch(path.Substring(1), nodeStack.Root, context);
int nextSep = path.IndexOf('/');
// make sure we don't stop on a ",/" which would be a value separator
// it's a hack that should be replaced with a proper regex for the whole node search
while (nextSep > 0 && path[nextSep - 1] == ',')
nextSep = path.IndexOf('/', nextSep + 1);
if (path[0] == '@')
{
if (nextSep < 2)
return null;
string subName = path.Substring(1, nextSep - 1);
string nodeType, nodeName;
if (subName.Contains("["))
{
// @NODETYPE[Name]/
nodeType = subName.Split('[')[0];
nodeName = subName.Split('[')[1].Replace("]", "");
}
else
{
// @NODETYPE/
nodeType = subName;
nodeName = null;
}
bool foundNodeType = false;
foreach (IProtoUrlConfig urlConfig in context.databaseConfigs)
{
ConfigNode node = urlConfig.Node;
if (node.name != nodeType) continue;
foundNodeType = true;
if (nodeName == null || (node.GetValue("name") is string testNodeName && WildcardMatch(testNodeName, nodeName)))
{
return RecurseVariableSearch(path.Substring(nextSep + 1), new NodeStack(node), context);
}
}
if (!foundNodeType) context.logger.Warning("Can't find nodeType:" + nodeType);
return null;
}
if (path.StartsWith("../"))
{
if (nodeStack.IsRoot)
return null;
return RecurseVariableSearch(path.Substring(3), nodeStack.Pop(), context);
}
// Node search
if (nextSep > 0 && path[nextSep - 1] != ',')
{
// Big case of code duplication here ...
// TODO : replace with a regex
string subName = path.Substring(0, nextSep);
string constraint = "";
string nodeType, nodeName;
int index = 0;
if (subName.Contains(":HAS[", out int hasStart))
{
constraint = subName.Substring(hasStart + 5, subName.LastIndexOf(']') - hasStart - 5);
subName = subName.Substring(0, hasStart);
}
else if (subName.Contains(','))
{
string tag = subName.Split(',')[1];
subName = subName.Split(',')[0];
int.TryParse(tag, out index);
}
if (subName.Contains("["))
{
// format NODETYPE[Name] {...}
// or NODETYPE[Name, index] {...}
nodeType = subName.Split('[')[0];
nodeName = subName.Split('[')[1].Replace("]", "");
}
else
{
// format NODETYPE {...}
nodeType = subName;
nodeName = null;
}
if (constraint.Length > 0)
{
// get the first one matching
ConfigNode last = null;
while (true)
{
ConfigNode n = FindConfigNodeIn(nodeStack.value, nodeType, nodeName, index++);
if (n == last || n == null)
break;
if (CheckConstraints(n, constraint))
return RecurseVariableSearch(path.Substring(nextSep + 1), nodeStack.Push(n), context);
last = n;
}
return null;
}
else
{
// just get one node
ConfigNode n = FindConfigNodeIn(nodeStack.value, nodeType, nodeName, index);
if (n != null)
return RecurseVariableSearch(path.Substring(nextSep + 1), nodeStack.Push(n), context);
return null;
}
}
// Value search
Match match = parseVarKey.Match(path);
if (!match.Success)
{
context.logger.Warning("Cannot parse variable search command: " + path);
return null;
}
string valName = match.Groups[1].Value;
int idx = 0;
if (match.Groups[2].Success)
int.TryParse(match.Groups[2].Value, out idx);
ConfigNode.Value cVal = FindValueIn(nodeStack.value, valName, idx);
if (cVal == null)
{
context.logger.Warning("Cannot find key " + valName + " in " + nodeStack.value.name);
return null;
}
if (match.Groups[3].Success)
{
ConfigNode.Value newVal = new ConfigNode.Value(cVal.name, cVal.value);
int.TryParse(match.Groups[3].Value, out int splitIdx);
char sep = ',';
if (match.Groups[4].Success)
sep = match.Groups[4].Value[0];
string[] split = newVal.value.Split(sep);
if (splitIdx < split.Length)
newVal.value = split[splitIdx];
else
newVal.value = "";
return newVal;
}
return cVal;
}
private static string ProcessVariableSearch(string value, NodeStack nodeStack, PatchContext context)
{
// value = #xxxx$yyyyy$zzzzz$aaaa$bbbb
// There is 2 or more '$'
if (value.Length > 0 && value[0] == '#' && value.IndexOf('$') != -1 && value.IndexOf('$') != value.LastIndexOf('$'))
{
//log("variable search input : =\"" + value + "\"");
string[] split = value.Split('$');
if (split.Length % 2 != 1)
return null;
StringBuilder builder = new StringBuilder();
builder.Append(split[0].Substring(1));
for (int i = 1; i < split.Length - 1; i += 2)
{
ConfigNode.Value result = RecurseVariableSearch(split[i], nodeStack, context);
if (result == null || result.value == null)
return null;
builder.Append(result.value);
builder.Append(split[i + 1]);
}
value = builder.ToString();
//log("variable search output : =\"" + value + "\"");
}
return value;
}
private static string FindAndReplaceValue(
ConfigNode mod,
ref string valName,
string value,
ConfigNode newNode,
Operator op,
int index,
out ConfigNode.Value origVal,
PatchContext context,
bool hasPosIndex = false,
int posIndex = 0,
bool hasPosStar = false,
char seperator = ',')
{
origVal = FindValueIn(newNode, valName, index);
if (origVal == null)
return null;
string oValue = origVal.value;
string[] strArray = new string[] { oValue };
if (hasPosIndex)
{
strArray = oValue.Split(new char[] { seperator }, StringSplitOptions.RemoveEmptyEntries);
if (posIndex >= strArray.Length)
{
context.progress.Error(context.patchUrl, "Invalid Vector Index!");
return null;
}
}
string backupValue = value;
while (posIndex < strArray.Length)
{
value = backupValue;
oValue = strArray[posIndex];
if (op != Operator.Assign)
{
if (op == Operator.RegexReplace)
{
try
{
string[] split = value.Split(value[0]);
Regex replace = regexCache.Fetch(split[1], delegate
{
return new Regex(split[1]);
});
value = replace.Replace(oValue, split[2]);
}
catch (Exception ex)
{
context.progress.Exception(context.patchUrl, "Error - Failed to do a regexp replacement: " + mod.name + " : original value=\"" + oValue +
"\" regexp=\"" + value +
"\" \nNote - to use regexp, the first char is used to subdivide the string (much like sed)", ex);
return null;
}
}
else if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out double s)
&& double.TryParse(oValue, NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out double os))
{
switch (op)
{
case Operator.Multiply:
value = (os * s).ToString(CultureInfo.InvariantCulture);
break;
case Operator.Divide:
value = (os / s).ToString(CultureInfo.InvariantCulture);
break;
case Operator.Add:
value = (os + s).ToString(CultureInfo.InvariantCulture);
break;
case Operator.Subtract:
value = (os - s).ToString(CultureInfo.InvariantCulture);
break;
case Operator.Exponentiate:
value = Math.Pow(os, s).ToString(CultureInfo.InvariantCulture);
break;
}
}
else
{
context.progress.Error(context.patchUrl, "Error - Failed to do a maths replacement: " + mod.name + " : original value=\"" + oValue +
"\" operator=" + op + " mod value=\"" + value + "\"");
return null;
}
}
strArray[posIndex] = value;
if (hasPosStar) posIndex++;
else break;
}
value = String.Join(new string(seperator, 1), strArray);
return value;
}
#endregion Applying Patches
#region Condition checking
// Split condiction while not getting lost in embeded brackets
public static List<string> SplitConstraints(string condition)
{
condition = condition.RemoveWS() + ",";
List<string> conditions = new List<string>();
int start = 0;
int level = 0;
for (int end = 0; end < condition.Length; end++)
{
if ((condition[end] == ',' || condition[end] == '&') && level == 0)
{
conditions.Add(condition.Substring(start, end - start));
start = end + 1;
}
else if (condition[end] == '[')
level++;
else if (condition[end] == ']')
level--;
}
return conditions;
}
static readonly char[] contraintSeparators = { '[', ']' };
public static bool CheckConstraints(ConfigNode node, string constraints)
{
constraints = constraints.RemoveWS();
if (constraints.Length == 0)
return true;
List<string> constraintList = SplitConstraints(constraints);
if (constraintList.Count == 1)
{
constraints = constraintList[0];
string remainingConstraints = "";
if (constraints.Contains(":HAS[", out int hasStart))
{
hasStart += 5;
remainingConstraints = constraints.Substring(hasStart, constraintList[0].LastIndexOf(']') - hasStart);
constraints = constraints.Substring(0, hasStart - 5);
}
string[] splits = constraints.Split(contraintSeparators, 3);
string type = splits[0].Substring(1);
string name = splits.Length > 1 ? splits[1] : null;
switch (constraints[0])
{
case '@':
case '!':
// @MODULE[ModuleAlternator] or !MODULE[ModuleAlternator]
bool not = (constraints[0] == '!');
bool any = false;
int index = 0;
ConfigNode last = null;
while (true)
{
ConfigNode subNode = FindConfigNodeIn(node, type, name, index++);
if (subNode == last || subNode == null)
break;
any = any || CheckConstraints(subNode, remainingConstraints);
last = subNode;
}
if (last != null)
{
//print("CheckConstraints: " + constraints + " " + (not ^ any));
return not ^ any;
}
//print("CheckConstraints: " + constraints + " " + (not ^ false));
return not ^ false;
case '#':
// #module[Winglet]
if (node.HasValue(type) && WildcardMatchValues(node, type, name))
{
bool ret2 = CheckConstraints(node, remainingConstraints);
//print("CheckConstraints: " + constraints + " " + ret2);
return ret2;
}
//print("CheckConstraints: " + constraints + " false");
return false;
case '~':
// ~breakingForce[] breakingForce is not present
// or: ~breakingForce[100] will be true if it's present but not 100, too.
if (name == "" && node.HasValue(type))
{
//print("CheckConstraints: " + constraints + " false");
return false;
}
if (name != "" && WildcardMatchValues(node, type, name))
{
//print("CheckConstraints: " + constraints + " false");
return false;
}
bool ret = CheckConstraints(node, remainingConstraints);
//print("CheckConstraints: " + constraints + " " + ret);
return ret;
default:
//print("CheckConstraints: " + constraints + " false");
return false;
}
}
bool ret3 = true;
foreach (string constraint in constraintList)
{
ret3 = ret3 && CheckConstraints(node, constraint);
}
//print("CheckConstraints: " + constraints + " " + ret3);
return ret3;
}
public static bool WildcardMatchValues(ConfigNode node, string type, string value)
{
double val = 0;
bool compare = value != null && value.Length > 1 && (value[0] == '<' || value[0] == '>');
compare = compare && double.TryParse(value.Substring(1), NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out val);
string[] values = node.GetValues(type);
for (int i = 0; i < values.Length; i++)
{
if (!compare && WildcardMatch(values[i], value))
return true;
if (compare && double.TryParse(values[i], NumberStyles.Float, CultureInfo.InvariantCulture.NumberFormat, out double val2)
&& ((value[0] == '<' && val2 < val) || (value[0] == '>' && val2 > val)))
{
return true;
}
}
return false;
}
public static bool WildcardMatch(string s, string wildcard)
{
if (wildcard == null)
return true;
string pattern = "^" + Regex.Escape(wildcard).Replace(@"\*", ".*").Replace(@"\?", ".") + "$";
Regex regex = regexCache.Fetch(pattern, delegate
{
return new Regex(pattern);
});
return regex.IsMatch(s);
}
#endregion Condition checking
#region Config Node Utilities
private static void InsertNode(ConfigNode newNode, ConfigNode subMod, int index)
{
string modName = subMod.name;
ConfigNode[] oldValues = newNode.GetNodes(modName);
if (index < oldValues.Length)
{
newNode.RemoveNodes(modName);
int i = 0;
for (; i < index; ++i)
newNode.AddNode(oldValues[i]);
newNode.AddNode(subMod);
for (; i < oldValues.Length; ++i)
newNode.AddNode(oldValues[i]);
}
else
newNode.AddNode(subMod);
}
private static void InsertValue(ConfigNode newNode, int index, string name, string value)
{
string[] oldValues = newNode.GetValues(name);
if (index < oldValues.Length)
{
newNode.RemoveValues(name);
int i = 0;
for (; i < index; ++i)
newNode.AddValueSafe(name, oldValues[i]);
newNode.AddValueSafe(name, value);
for (; i < oldValues.Length; ++i)
newNode.AddValueSafe(name, oldValues[i]);
return;
}
newNode.AddValueSafe(name, value);
}
//FindConfigNodeIn finds and returns a ConfigNode in src of type nodeType.
//If nodeName is not null, it will only find a node of type nodeType with the value name=nodeName.
//If nodeTag is not null, it will only find a node of type nodeType with the value name=nodeName and tag=nodeTag.
public static ConfigNode FindConfigNodeIn(
ConfigNode src,
string nodeType,
string nodeName = null,
int index = 0)
{
List<ConfigNode> nodes = new List<ConfigNode>();
int c = src.nodes.Count;
for(int i = 0; i < c; ++i)
{
if (WildcardMatch(src.nodes[i].name, nodeType))
nodes.Add(src.nodes[i]);
}
int nodeCount = nodes.Count;
if (nodeCount == 0)
return null;
if (nodeName == null)
{
if (index >= 0)
return nodes[Math.Min(index, nodeCount - 1)];
return nodes[Math.Max(0, nodeCount + index)];
}
ConfigNode last = null;
if (index >= 0)
{
for (int i = 0; i < nodeCount; ++i)
{
if (nodes[i].HasValue("name") && WildcardMatch(nodes[i].GetValue("name"), nodeName))
{
last = nodes[i];
if (--index < 0)
return last;
}
}
return last;
}
for (int i = nodeCount - 1; i >= 0; --i)
{
if (nodes[i].HasValue("name") && WildcardMatch(nodes[i].GetValue("name"), nodeName))
{
last = nodes[i];
if (++index >= 0)
return last;
}
}
return last;
}
private static ConfigNode.Value FindValueIn(ConfigNode newNode, string valName, int index)
{
ConfigNode.Value v = null;
for (int i = 0; i < newNode.values.Count; ++i)
{
if (WildcardMatch(newNode.values[i].name, valName))
{
v = newNode.values[i];
if (--index < 0)
return v;
}
}
return v;
}
#endregion Config Node Utilities
}
}
================================================
FILE: ModuleManager/MMPatchRunner.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using ModuleManager.Collections;
using ModuleManager.Extensions;
using ModuleManager.Logging;
using ModuleManager.Threading;
using static ModuleManager.FilePathRepository;
namespace ModuleManager
{
public class MMPatchRunner
{
private readonly IBasicLogger kspLogger;
public string Status { get; private set; } = "";
public string Errors { get; private set; } = "";
public MMPatchRunner(IBasicLogger kspLogger)
{
this.kspLogger = kspLogger ?? throw new ArgumentNullException(nameof(kspLogger));
}
public IEnumerator Run()
{
PostPatchLoader.Instance.databaseConfigs = null;
if (!Directory.Exists(logsDirPath)) Directory.CreateDirectory(logsDirPath);
kspLogger.Info("Patching started on a new thread, all output will be directed to " + logPath);
MessageQueue<ILogMessage> mmLogQueue = new MessageQueue<ILogMessage>();
QueueLogRunner logRunner = new QueueLogRunner(mmLogQueue);
ITaskStatus loggingThreadStatus = BackgroundTask.Start(delegate
{
using StreamLogger streamLogger = new StreamLogger(new FileStream(logPath, FileMode.Create));
streamLogger.Info("Log started at " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff"));
logRunner.Run(streamLogger);
streamLogger.Info("Done!");
});
// Wait for game database to be initialized for the 2nd time and wait for any plugins to initialize
yield return null;
yield return null;
IBasicLogger mmLogger = new QueueLogger(mmLogQueue);
IEnumerable<ModListGenerator.ModAddedByAssembly> modsAddedByAssemblies = ModListGenerator.GetAdditionalModsFromStaticMethods(mmLogger);
IEnumerable<IProtoUrlConfig> databaseConfigs = null;
MMPatchLoader patchLoader = new MMPatchLoader(modsAddedByAssemblies, mmLogger);
ITaskStatus patchingThreadStatus = BackgroundTask.Start(delegate
{
databaseConfigs = patchLoader.Run();
});
while(true)
{
yield return null;
if (!patchingThreadStatus.IsRunning)
logRunner.RequestStop();
Status = patchLoader.status;
Errors = patchLoader.errors;
if (!patchingThreadStatus.IsRunning && !loggingThreadStatus.IsRunning) break;
}
if (patchingThreadStatus.IsExitedWithError)
{
kspLogger.Exception("The patching thread threw an exception", patchingThreadStatus.Exception);
FatalErrorHandler.HandleFatalError("The patching thread threw an exception");
yield break;
}
if (loggingThreadStatus.IsExitedWithError)
{
kspLogger.Exception("The logging thread threw an exception", loggingThreadStatus.Exception);
FatalErrorHandler.HandleFatalError("The logging thread threw an exception");
yield break;
}
if (databaseConfigs == null)
{
kspLogger.Error("The patcher returned a null collection of configs");
FatalErrorHandler.HandleFatalError("The patcher returned a null collection of configs");
yield break;
}
PostPatchLoader.Instance.databaseConfigs = databaseConfigs;
}
}
}
================================================
FILE: ModuleManager/ModListGenerator.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Reflection;
using UnityEngine;
using ModuleManager.Extensions;
using ModuleManager.Logging;
using ModuleManager.Utils;
using ModuleManager.Progress;
namespace ModuleManager
{
public static class ModListGenerator
{
public static IEnumerable<string> GenerateModList(IEnumerable<ModAddedByAssembly> modsAddedByAssemblies, IPatchProgress progress, IBasicLogger logger)
{
#region List of mods
//string envInfo = "ModuleManager env info\n";
//envInfo += " " + Environment.OSVersion.Platform + " " + ModuleManager.intPtr.ToInt64().ToString("X16") + "\n";
//envInfo += " " + Convert.ToString(ModuleManager.intPtr.ToInt64(), 2) + " " + Convert.ToString(ModuleManager.intPtr.ToInt64() >> 63, 2) + "\n";
//string gamePath = Environment.GetCommandLineArgs()[0];
//envInfo += " Args: " + gamePath.Split(Path.DirectorySeparatorChar).Last() + " " + string.Join(" ", Environment.GetCommandLineArgs().Skip(1).ToArray()) + "\n";
//envInfo += " Executable SHA256 " + FileSHA(gamePath);
//
//log(envInfo);
List<string> mods = new List<string>();
StringBuilder modListInfo = new StringBuilder();
modListInfo.Append("compiling list of loaded mods...\nMod DLLs found:\n");
string format = " {0,-40}{1,-25}{2,-25}{3,-25}{4}\n";
modListInfo.AppendFormat(
format,
"Name",
"Assembly Version",
"Assembly File Version",
"KSPAssembly Version",
"SHA256"
);
modListInfo.Append('\n');
foreach (AssemblyLoader.LoadedAssembly mod in AssemblyLoader.loadedAssemblies)
{
if (string.IsNullOrEmpty(mod.assembly.Location)) //Diazo Edit for xEvilReeperx AssemblyReloader mod
continue;
FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(mod.assembly.Location);
AssemblyName assemblyName = mod.assembly.GetName();
string kspAssemblyVersion;
if (mod.versionMajor == 0 && mod.versionMinor == 0)
kspAssemblyVersion = "";
else
kspAssemblyVersion = mod.versionMajor + "." + mod.versionMinor;
string fileSha = "";
try
{
fileSha = FileUtils.FileSHA(mod.assembly.Location);
}
catch (Exception e)
{
progress.Exception("Exception while generating SHA for assembly " + assemblyName.Name, e);
}
modListInfo.AppendFormat(
format,
assemblyName.Name,
assemblyName.Version,
fileVersionInfo.FileVersion,
kspAssemblyVersion,
fileSha
);
// modlist += String.Format(" {0,-50} SHA256 {1}\n", modInfo, FileSHA(mod.assembly.Location));
if (!mods.Contains(assemblyName.Name, StringComparer.OrdinalIgnoreCase))
mods.Add(assemblyName.Name);
}
modListInfo.Append("Non-DLL mods added (:FOR[xxx]):\n");
foreach (UrlDir.UrlConfig cfgmod in GameDatabase.Instance.root.AllConfigs)
{
if (CommandParser.Parse(cfgmod.type, out string name) != Command.Insert)
{
if (name.Contains(":FOR["))
{
name = name.RemoveWS();
// check for FOR[] blocks that don't match loaded DLLs and add them to the pass list
try
{
string dependency = name.Substring(name.IndexOf(":FOR[") + 5);
dependency = dependency.Substring(0, dependency.IndexOf(']'));
if (!mods.Contains(dependency, StringComparer.OrdinalIgnoreCase))
{
// found one, now add it to the list.
mods.Add(dependency);
modListInfo.AppendFormat(" {0}\n", dependency);
}
}
catch (ArgumentOutOfRangeException)
{
progress.Error(cfgmod, "Skipping :FOR init for line " + name +
". The line most likely contains a space that should be removed");
}
}
}
}
modListInfo.Append("Mods by directory (sub directories of GameData):\n");
UrlDir gameData = GameDatabase.Instance.root.children.First(dir => dir.type == UrlDir.DirectoryType.GameData);
foreach (UrlDir subDir in gameData.children)
{
string cleanName = subDir.name.RemoveWS();
if (!mods.Contains(cleanName, StringComparer.OrdinalIgnoreCase))
{
mods.Add(cleanName);
modListInfo.AppendFormat(" {0}\n", cleanName);
}
}
modListInfo.Append("Mods added by assemblies:\n");
foreach (ModAddedByAssembly mod in modsAddedByAssemblies)
{
if (!mods.Contains(mod.modName, StringComparer.OrdinalIgnoreCase))
{
mods.Add(mod.modName);
modListInfo.AppendFormat(" {0}\n", mod);
}
}
logger.Info(modListInfo.ToString());
mods.Sort();
#endregion List of mods
return mods;
}
public class ModAddedByAssembly
{
public readonly string modName;
public readonly string assemblyName;
public ModAddedByAssembly(string modName, string assemblyName)
{
this.modName = modName ?? throw new ArgumentNullException(nameof(modName));
this.assemblyName = assemblyName ?? throw new ArgumentNullException(nameof(assemblyName));
}
public override string ToString()
{
return $"{modName} (added by {assemblyName})";
}
}
public static IEnumerable<ModAddedByAssembly> GetAdditionalModsFromStaticMethods(IBasicLogger logger)
{
List<ModAddedByAssembly> result = new List<ModAddedByAssembly>();
foreach (Assembly ass in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
foreach (Type type in ass.GetTypes())
{
MethodInfo method = type.GetMethod("ModuleManagerAddToModList", BindingFlags.Public | BindingFlags.Static);
if (method != null && method.GetParameters().Length == 0 && typeof(IEnumerable<string>).IsAssignableFrom(method.ReturnType))
{
string methodName = $"{ass.GetName().Name}.{type.Name}.{method.Name}()";
try
{
logger.Info("Calling " + methodName);
IEnumerable<string> modsToAdd = (IEnumerable<string>)method.Invoke(null, null);
if (modsToAdd == null)
{
logger.Error("ModuleManagerAddToModList returned null: " + methodName);
continue;
}
foreach (string mod in modsToAdd)
{
result.Add(new ModAddedByAssembly(mod, ass.GetName().Name));
}
}
catch (Exception e)
{
logger.Exception("Exception while calling " + methodName, e);
}
}
}
}
catch (Exception e)
{
logger.Exception("Add to mod list threw an exception in loading " + ass.FullName, e);
}
}
foreach (MonoBehaviour obj in UnityEngine.Object.FindObjectsOfType<MonoBehaviour>())
{
MethodInfo method = obj.GetType().GetMethod("ModuleManagerAddToModList", BindingFlags.Public | BindingFlags.Instance);
if (method != null && method.GetParameters().Length == 0 && typeof(IEnumerable<string>).IsAssignableFrom(method.ReturnType))
{
string methodName = $"{obj.GetType().Name}.{method.Name}()";
try
{
logger.Info("Calling " + methodName);
IEnumerable<string> modsToAdd = (IEnumerable<string>)method.Invoke(obj, null);
if (modsToAdd == null)
{
logger.Error("ModuleManagerAddToModList returned null: " + methodName);
continue;
}
foreach (string mod in modsToAdd)
{
result.Add(new ModAddedByAssembly(mod, obj.GetType().Assembly.GetName().Name));
}
}
catch (Exception e)
{
logger.Exception("Exception while calling " + methodName, e);
}
}
}
return result;
}
}
}
================================================
FILE: ModuleManager/ModuleManager.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using TMPro;
using UnityEngine;
using Debug = UnityEngine.Debug;
using ModuleManager.Cats;
using ModuleManager.Extensions;
using ModuleManager.Logging;
using ModuleManager.UnityLogHandle;
namespace ModuleManager
{
[KSPAddon(KSPAddon.Startup.Instantly, true)]
public class ModuleManager : MonoBehaviour
{
#region state
private bool inRnDCenter;
public bool showUI = false;
private float textPos = 0;
//private Texture2D tex;
//private Texture2D tex2;
private bool nyan = false;
private bool nCats = false;
public static bool dumpPostPatch = false;
public static bool DontCopyLogs { get; private set; } = false;
private PopupDialog menu;
private MMPatchRunner patchRunner;
#endregion state
private static bool loadedInScene;
internal void OnRnDCenterSpawn()
{
inRnDCenter = true;
}
internal void OnRnDCenterDeSpawn()
{
inRnDCenter = false;
}
public static void Log(String s)
{
print("[ModuleManager] " + s);
}
private readonly Stopwatch totalTime = new Stopwatch();
internal void Awake()
{
if (LoadingScreen.Instance == null)
{
Destroy(gameObject);
return;
}
// Ensure that only one copy of the service is run per scene change.
if (loadedInScene || !ElectionAndCheck())
{
Assembly currentAssembly = Assembly.GetExecutingAssembly();
Log("Multiple copies of current version. Using the first copy. Version: " +
currentAssembly.GetName().Version);
Destroy(gameObject);
return;
}
totalTime.Start();
Debug.unityLogger.logHandler = new InterceptLogHandler(Debug.unityLogger.logHandler);
// Allow loading the background in the loading screen
Application.runInBackground = true;
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = -1;
// More cool loading screen. Less 4 stoke logo.
for (int i = 0; i < LoadingScreen.Instance.Screens.Count; i++)
{
var state = LoadingScreen.Instance.Screens[i];
state.fadeInTime = i < 3 ? 0.1f : 1;
state.displayTime = i < 3 ? 1 : 3;
state.fadeOutTime = i < 3 ? 0.1f : 1;
}
TextMeshProUGUI[] texts = LoadingScreen.Instance.gameObject.GetComponentsInChildren<TextMeshProUGUI>();
foreach (var text in texts)
{
textPos = Mathf.Min(textPos, text.rectTransform.localPosition.y);
}
DontDestroyOnLoad(gameObject);
// Subscribe to the RnD center spawn/deSpawn events
GameEvents.onGUIRnDComplexSpawn.Add(OnRnDCenterSpawn);
GameEvents.onGUIRnDComplexDespawn.Add(OnRnDCenterDeSpawn);
LoadingScreen screen = FindObjectOfType<LoadingScreen>();
if (screen == null)
{
Log("Can't find LoadingScreen type. Aborting ModuleManager execution");
return;
}
List<LoadingSystem> list = LoadingScreen.Instance.loaders;
if (list != null)
{
// So you can insert a LoadingSystem object in this list at any point.
// GameDatabase is first in the list, and PartLoader is second
// We could insert ModuleManager after GameDatabase to get it to run there
// and SaveGameFixer after PartLoader.
int gameDatabaseIndex = list.FindIndex(s => s is GameDatabase);
GameObject aGameObject = new GameObject("ModuleManager");
DontDestroyOnLoad(aGameObject);
Log(string.Format("Adding post patch to the loading screen {0}", list.Count));
list.Insert(gameDatabaseIndex + 1, aGameObject.AddComponent<PostPatchLoader>());
patchRunner = new MMPatchRunner(new PrefixLogger("ModuleManager", new UnityLogger(Debug.unityLogger)));
StartCoroutine(patchRunner.Run());
// Workaround for 1.6.0 Editor bug after a PartDatabase rebuild.
if (Versioning.version_major == 1 && Versioning.version_minor == 6 && Versioning.Revision == 0)
{
Fix16 fix16 = aGameObject.AddComponent<Fix16>();
list.Add(fix16);
}
}
bool foolsDay = (DateTime.Now.Month == 4 && DateTime.Now.Day == 1);
bool catDay = (DateTime.Now.Month == 2 && DateTime.Now.Day == 22);
nyan = foolsDay
|| Environment.GetCommandLineArgs().Contains("-nyan-nyan");
nCats = catDay
|| Environment.GetCommandLineArgs().Contains("-ncats");
dumpPostPatch = Environment.GetCommandLineArgs().Contains("-mm-dump");
DontCopyLogs = Environment.GetCommandLineArgs().Contains("-mm-dont-copy-logs");
loadedInScene = true;
}
private TextMeshProUGUI status;
private TextMeshProUGUI errors;
private TextMeshProUGUI warning;
[SuppressMessage("Code Quality", "IDE0051", Justification = "Called by Unity")]
private void Start()
{
if (nCats)
CatManager.LaunchCats();
else if (nyan)
CatManager.LaunchCat();
Canvas canvas = LoadingScreen.Instance.GetComponentInChildren<Canvas>();
status = CreateTextObject(canvas, "MMStatus");
errors = CreateTextObject(canvas, "MMErrors");
warning = CreateTextObject(canvas, "MMWarning");
warning.text = "";
//if (Versioning.version_major == 1 && Versioning.version_minor == 0 && Versioning.Revision == 5 && Versioning.BuildID == 1024)
//{
// warning.text = "Your KSP 1.0.5 is running on build 1024. You should upgrade to build 1028 to avoid problems with addons.";
// //if (GUI.Button(new Rect(Screen.width / 2f - 100, offsetY, 200, 20), "Click to open the Forum thread"))
// // Application.OpenURL("http://forum.kerbalspaceprogram.com/index.php?/topic/124998-silent-patch-for-ksp-105-published/");
//}
if (Versioning.version_major == 1 && Versioning.version_minor >= 8)
{
foreach (AssemblyLoader.LoadedAssembly assembly in AssemblyLoader.loadedAssemblies)
{
AssemblyName assemblyName = assembly.assembly.GetName();
if (assemblyName.Name == "Firespitter" && assemblyName.Version <= Version.Parse("7.3.7175.38653"))
{
warning.text = "You are using a version of Firespitter that does not run properly on KSP 1.8+\nThis version may prevent the game from loading properly and may create problems for other mods";
}
}
}
}
private TextMeshProUGUI CreateTextObject(Canvas canvas, string name)
{
GameObject statusGameObject = new GameObject(name);
TextMeshProUGUI text = statusGameObject.AddComponent<TextMeshProUGUI>();
text.text = "STATUS";
text.fontSize = 18;
text.autoSizeTextContainer = true;
text.font = Resources.Load("Fonts/Calibri SDF", typeof(TMP_FontAsset)) as TMP_FontAsset;
text.alignment = TextAlignmentOptions.Center;
text.enableWordWrapping = false;
text.isOverlay = true;
text.rectTransform.anchorMin = new Vector2(0.5f, 0);
text.rectTransform.anchorMax = new Vector2(0.5f, 0);
text.rectTransform.anchoredPosition = Vector2.zero;
statusGameObject.transform.SetParent(canvas.transform);
return text;
}
// Unsubscribe from events when the behavior dies
internal void OnDestroy()
{
GameEvents.onGUIRnDComplexSpawn.Remove(OnRnDCenterSpawn);
GameEvents.onGUIRnDComplexDespawn.Remove(OnRnDCenterDeSpawn);
}
internal void Update()
{
if (GameSettings.MODIFIER_KEY.GetKey() && Input.GetKeyDown(KeyCode.F11)
&& (HighLogic.LoadedScene == GameScenes.SPACECENTER || HighLogic.LoadedScene == GameScenes.MAINMENU)
&& !inRnDCenter)
{
if (menu == null)
{
menu = PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f),
new Vector2(0.5f, 0.5f),
new MultiOptionDialog(
"ModuleManagerMenu",
"",
"ModuleManager",
HighLogic.UISkin,
new Rect(0.5f, 0.5f, 150f, 60f),
new DialogGUIFlexibleSpace(),
new DialogGUIVerticalLayout(
new DialogGUIFlexibleSpace(),
new DialogGUIButton("Reload Database",
delegate
{
MMPatchLoader.keepPartDB = false;
StartCoroutine(DataBaseReloadWithMM());
}, 140.0f, 30.0f, true),
new DialogGUIButton("Quick Reload Database",
delegate
{
MMPatchLoader.keepPartDB = true;
StartCoroutine(DataBaseReloadWithMM());
}, 140.0f, 30.0f, true),
new DialogGUIButton("Dump Database to Files",
delegate
{
StartCoroutine(DataBaseReloadWithMM(true));
}, 140.0f, 30.0f, true),
new DialogGUIButton("Close", () => { }, 140.0f, 30.0f, true)
)),
false,
HighLogic.UISkin);
}
else
{
menu.Dismiss();
menu = null;
}
}
if (totalTime.IsRunning && HighLogic.LoadedScene == GameScenes.MAINMENU)
{
totalTime.Stop();
Log("Total loading Time = " + ((float)totalTime.ElapsedMilliseconds / 1000).ToString("F3") + "s");
Application.runInBackground = GameSettings.SIMULATE_IN_BACKGROUND;
QualitySettings.vSyncCount = GameSettings.SYNC_VBL;
Application.targetFrameRate = GameSettings.FRAMERATE_LIMIT;
}
float offsetY = textPos;
float h;
if (patchRunner != null)
{
if (warning)
{
warning.text = InterceptLogHandler.Warnings;
h = warning.text.Length > 0 ? warning.textBounds.size.y : 0;
offsetY += h;
warning.rectTransform.localPosition = new Vector3(0, offsetY);
}
if (status)
{
status.text = patchRunner.Status;
h = status.text.Length > 0 ? status.textBounds.size.y : 0;
offsetY += h;
status.transform.localPosition = new Vector3(0, offsetY);
}
if (errors)
{
errors.text = patchRunner.Errors;
h = errors.text.Length > 0 ? errors.textBounds.size.y : 0;
offsetY += h;
errors.transform.localPosition = new Vector3(0, offsetY);
}
}
}
#region GUI stuff.
internal static IntPtr intPtr = new IntPtr(long.MaxValue);
/* Not required anymore. At least
public static bool IsABadIdea()
{
return (intPtr.ToInt64() == long.MaxValue) && (Environment.OSVersion.Platform == PlatformID.Win32NT);
}
*/
private IEnumerator DataBaseReloadWithMM(bool dump = false)
{
QualitySettings.vSyncCount = 0;
Application.targetFrameRate = -1;
patchRunner = new MMPatchRunner(new PrefixLogger("ModuleManager", new UnityLogger(Debug.unityLogger)));
float totalLoadWeight = GameDatabase.Instance.LoadWeight() + PartLoader.Instance.LoadWeight();
bool startedReload = false;
UISkinDef skinDef = HighLogic.UISkin;
UIStyle centeredTextStyle = new UIStyle(skinDef.label)
{
alignment = TextAnchor.UpperCenter
};
PopupDialog reloadingDialog = PopupDialog.SpawnPopupDialog(new Vector2(0.5f, 0.5f),
new Vector2(0.5f, 0.5f),
new MultiOptionDialog(
"ModuleManagerReloading",
"",
"ModuleManager - Reloading Database",
skinDef,
new Rect(0.5f, 0.5f, 600f, 60f),
new DialogGUIFlexibleSpace(),
new DialogGUIVerticalLayout(
new DialogGUIFlexibleSpace(),
new DialogGUILabel(delegate ()
{
float progressFraction;
if (!startedReload)
{
progressFraction = 0f;
}
else if (!GameDatabase.Instance.IsReady() || !PostPatchLoader.Instance.IsReady())
{
progressFraction = GameDatabase.Instance.ProgressFraction() * GameDatabase.Instance.LoadWeight();
progressFraction /= totalLoadWeight;
}
else if (!PartLoader.Instance.IsReady())
{
progressFraction = GameDatabase.Instance.LoadWeight() + (PartLoader.Instance.ProgressFraction() * GameDatabase.Instance.LoadWeight());
progressFraction /= totalLoadWeight;
}
else
{
progressFraction = 1f;
}
return $"Overall progress: {progressFraction:P0}";
}, centeredTextStyle, expandW: true),
new DialogGUILabel(delegate ()
{
if (!startedReload)
return "Starting";
else if (!GameDatabase.Instance.IsReady())
return GameDatabase.Instance.ProgressTitle();
else if (!PostPatchLoader.Instance.IsReady())
return PostPatchLoader.Instance.ProgressTitle();
else if (!PartLoader.Instance.IsReady())
return PartLoader.Instance.ProgressTitle();
else
return "";
}),
new DialogGUISpace(5f),
new DialogGUILabel(() => patchRunner.Status)
)
),
false,
skinDef);
yield return null;
GameDatabase.Instance.Recompile = true;
GameDatabase.Instance.StartLoad();
startedReload = true;
yield return null;
StartCoroutine(patchRunner.Run());
// wait for it to finish
while (!GameDatabase.Instance.IsReady())
yield return null;
PostPatchLoader.Instance.StartLoad();
while (!PostPatchLoader.Instance.IsReady())
yield return null;
if (dump)
OutputAllConfigs();
PartLoader.Instance.StartLoad();
while (!PartLoader.Instance.IsReady())
yield return null;
// Needs more work.
//ConfigNode game = HighLogic.CurrentGame.config.GetNode("GAME");
//if (game != null && ResearchAndDevelopment.Instance != null)
//{
// ScreenMessages.PostScreenMessage("GAME found");
// ConfigNode scenario = game.GetNodes("SCENARIO").FirstOrDefault((ConfigNode n) => n.name == "ResearchAndDevelopment");
// if (scenario != null)
// {
// ScreenMessages.PostScreenMessage("SCENARIO found");
// ResearchAndDevelopment.Instance.OnLoad(scenario);
// }
//}
QualitySettings.vSyncCount = GameSettings.SYNC_VBL;
Application.targetFrameRate = GameSettings.FRAMERATE_LIMIT;
reloadingDialog.Dismiss();
}
public static void OutputAllConfigs()
{
string path = Path.GetFullPath(Path.Combine(KSPUtil.ApplicationRootPath, "_MMCfgOutput"));
try
{
Directory.CreateDirectory(path);
foreach (string file in Directory.GetFiles(path))
{
File.Delete(file);
}
foreach (string dir in Directory.GetDirectories(path))
{
Directory.Delete(dir, true);
}
}
catch (IOException ioException)
{
Log("Exception while cleaning the export dir\n" + ioException);
}
catch (UnauthorizedAccessException unauthorizedAccessException)
{
Log("Exception while cleaning the export dir\n" + unauthorizedAccessException);
}
static void WriteDirectoryRecursive(UrlDir currentDir, string dirPath)
{
if (currentDir.files.Count > 0) Directory.CreateDirectory(dirPath);
foreach (UrlDir.UrlFile urlFile in currentDir.files)
{
if (urlFile.fileType != UrlDir.FileType.Config) continue;
Log("Exporting " + urlFile.GetUrlWithExtension());
string filePath = Path.Combine(dirPath, urlFile.GetNameWithExtension());
bool first = true;
using FileStream stream = new FileStream(filePath, FileMode.Create);
using StreamWriter writer = new StreamWriter(stream);
foreach (UrlDir.UrlConfig urlConfig in urlFile.configs)
{
try
{
if (first) first = false;
else writer.Write("\n");
ConfigNode copy = urlConfig.config.DeepCopy();
copy.EscapeValuesRecursive();
writer.Write(copy.ToString());
}
catch (Exception e)
{
Log("Exception while trying to write the file " + filePath + "\n" + e);
}
}
}
foreach (UrlDir urlDir in currentDir.children)
{
WriteDirectoryRecursive(urlDir, Path.Combine(dirPath, urlDir.name));
}
}
try
{
WriteDirectoryRecursive(GameDatabase.Instance.root, path);
}
catch (DirectoryNotFoundException directoryNotFoundException)
{
Log("Exception while exporting the cfg\n" + directoryNotFoundException);
}
catch (IOException ioException)
{
Log("Exception while exporting the cfg\n" + ioException);
}
catch (UnauthorizedAccessException unauthorizedAccessException)
{
Log("Exception while exporting the cfg\n" + unauthorizedAccessException);
}
}
#endregion GUI stuff.
public bool ElectionAndCheck()
{
#region Type election
// TODO : Move the old version check in a process that call Update.
// Check for old version and MMSarbianExt
IEnumerable<AssemblyLoader.LoadedAssembly> oldMM =
AssemblyLoader.loadedAssemblies.Where(
a => a.assembly.GetName().Name == Assembly.GetExecutingAssembly().GetName().Name)
.Where(a => a.assembly.GetName().Version.CompareTo(new System.Version(1, 5, 0)) == -1);
IEnumerable<AssemblyLoader.LoadedAssembly> oldAssemblies =
oldMM.Concat(AssemblyLoader.loadedAssemblies.Where(a => a.assembly.GetName().Name == "MMSarbianExt"));
if (oldAssemblies.Any())
{
IEnumerable<string> badPaths =
oldAssemblies.Select(a => a.path)
.Select(
p =>
Uri.UnescapeDataString(
new Uri(Path.GetFullPath(KSPUtil.ApplicationRootPath)).MakeRelativeUri(new Uri(p))
.ToString()
.Replace('/', Path.DirectorySeparatorChar)));
string status =
"You have old versions of Module Manager (older than 1.5) or MMSarbianExt.\nYou will need to remove them for Module Manager and the mods using it to work\nExit KSP and delete those files :\n" +
String.Join("\n", badPaths.ToArray());
PopupDialog.SpawnPopupDialog(new Vector2(0f, 1f), new Vector2(0f, 1f), "ModuleManagerOldVersions", "Old versions of Module Manager", status, "OK", false, UISkinManager.defaultSkin);
Log("Old version of Module Manager present. Stopping");
return false;
}
//PopupDialog.SpawnPopupDialog(new Vector2(0.1f, 1f), new Vector2(0.2f, 1f), "Test of the dialog", "Stuff", "OK", false, UISkinManager.defaultSkin);
Assembly currentAssembly = Assembly.GetExecutingAssembly();
IEnumerable<AssemblyLoader.LoadedAssembly> eligible = from a in AssemblyLoader.loadedAssemblies
let ass = a.assembly
where ass.GetName().Name == currentAssembly.GetName().Name
orderby ass.GetName().Version descending, a.path ascending
select a;
// Elect the newest loaded version of MM to process all patch files.
// If there is a newer version loaded then don't do anything
// If there is a same version but earlier in the list, don't do anything either.
if (eligible.First().assembly != currentAssembly)
{
//loaded = true;
Log("version " + currentAssembly.GetName().Version + " at " + currentAssembly.Location +
" lost the election");
Destroy(gameObject);
return false;
}
string candidates = "";
foreach (AssemblyLoader.LoadedAssembly a in eligible)
{
if (currentAssembly.Location != a.path)
candidates += "Version " + a.assembly.GetName().Version + " " + a.path + " " + "\n";
}
if (candidates.Length > 0)
{
Log("version " + currentAssembly.GetName().Version + " at " + currentAssembly.Location +
" won the election against\n" + candidates);
}
#endregion Type election
return true;
}
}
}
================================================
FILE: ModuleManager/ModuleManager.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>10.0.0</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{02C8E3AF-69F9-4102-AB60-DD6DE60662D3}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>ModuleManager</RootNamespace>
<AssemblyName>ModuleManager</AssemblyName>
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>False</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<LangVersion>default</LangVersion>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>none</DebugType>
<Optimize>True</Optimize>
<OutputPath>bin\Release\</OutputPath>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<ConsolePause>False</ConsolePause>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<LangVersion>8.0</LangVersion>
</PropertyGroup>
<ItemGroup>
<Compile Include="Cats\CatAnimator.cs" />
<Compile Include="Cats\CatManager.cs" />
<Compile Include="Cats\CatMover.cs" />
<Compile Include="Cats\CatOrbiter.cs" />
<Compile Include="Collections\ArrayEnumerator.cs" />
<Compile Include="Collections\ImmutableStack.cs" />
<Compile Include="Collections\KeyValueCache.cs" />
<Compile Include="Collections\MessageQueue.cs" />
<Compile Include="Command.cs" />
<Compile Include="CommandParser.cs" />
<Compile Include="Extensions\ByteArrayExtensions.cs" />
<Compile Include="Extensions\ConfigNodeExtensions.cs" />
<Compile Include="Extensions\IBasicLoggerExtensions.cs" />
<Compile Include="Extensions\NodeStackExtensions.cs" />
<Compile Include="Extensions\StringExtensions.cs" />
<Compile Include="Extensions\UrlConfigExtensions.cs" />
<Compile Include="Extensions\UrlDirExtensions.cs" />
<Compile Include="Extensions\UrlFileExtensions.cs" />
<Compile Include="FatalErrorHandler.cs" />
<Compile Include="FilePathRepository.cs" />
<Compile Include="Fix16.cs" />
<Compile Include="Logging\IBasicLogger.cs" />
<Compile Include="Logging\ILogMessage.cs" />
<Compile Include="Logging\LogMessage.cs" />
<Compile Include="Logging\LogSplitter.cs" />
<Compile Include="Logging\PrefixLogger.cs" />
<Compile Include="Logging\QueueLogger.cs" />
<Compile Include="Logging\QueueLogRunner.cs" />
<Compile Include="Logging\StreamLogger.cs" />
<Compile Include="Logging\UnityLogger.cs" />
<Compile Include="MMPatchLoader.cs" />
<Compile Include="MMPatchRunner.cs" />
<Compile Include="ModuleManager.cs" />
<Compile Include="ModListGenerator.cs" />
<Compile Include="PostPatchLoader.cs" />
<Compile Include="ModuleManagerTestRunner.cs" />
<Compile Include="NeedsChecker.cs" />
<Compile Include="NodeMatcher.cs" />
<Compile Include="Operator.cs" />
<Compile Include="OperatorParser.cs" />
<Compile Include="Pass.cs" />
<Compile Include="PatchApplier.cs" />
<Compile Include="PatchContext.cs" />
<Compile Include="Patches\CopyPatch.cs" />
<Compile Include="Patches\DeletePatch.cs" />
<Compile Include="Patches\EditPatch.cs" />
<Compile Include="Patches\InsertPatch.cs" />
<Compile Include="Patches\IPatch.cs" />
<Compile Include="Patches\PassSpecifiers\AfterPassSpecifier.cs" />
<Compile Include="Patches\PassSpecifiers\BeforePassSpecifier.cs" />
<Compile Include="Patches\PassSpecifiers\FinalPassSpecifier.cs" />
<Compile Include="Patches\PassSpecifiers\FirstPassSpecifier.cs" />
<Compile Include="Patches\PassSpecifiers\ForPassSpecifier.cs" />
<Compile Include="Patches\PassSpecifiers\InsertPassSpecifier.cs" />
<Compile Include="Patches\PassSpecifiers\IPassSpecifier.cs" />
<Compile Include="Patches\PassSpecifiers\LegacyPassSpecifier.cs" />
<Compile Include="Patches\PassSpecifiers\LastPassSpecifier.cs" />
<Compile Include="Patches\PatchCompiler.cs" />
<Compile Include="Patches\ProtoPatch.cs" />
<Compile Include="Patches\ProtoPatchBuilder.cs" />
<Compile Include="PatchExtractor.cs" />
<Compile Include="PatchList.cs" />
<Compile Include="Progress\ProgressCounter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs" />
<Compile Include="Progress\IPatchProgress.cs" />
<Compile Include="Progress\PatchProgress.cs" />
<Compile Include="ProtoUrlConfig.cs" />
<Compile Include="Tags\Tag.cs" />
<Compile Include="Tags\TagList.cs" />
<Compile Include="Tags\TagListParser.cs" />
<Compile Include="Threading\ITaskStatus.cs" />
<Compile Include="Threading\TaskStatus.cs" />
<Compile Include="Threading\TaskStatusWrapper.cs" />
<Compile Include="ExceptionIntercept\InterceptLogHandler.cs" />
<Compile Include="Utils\Counter.cs" />
<Compile Include="Utils\FileUtils.cs" />
<Compile Include="CustomConfigsManager.cs" />
<Compile Include="Threading\BackgroundTask.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Assembly-CSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="KSPAssets, Version=1.3.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
</Reference>
<Reference Include="UnityEngine, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.ImageConversionModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.InputLegacyModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.Physics2DModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.TextRenderingModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UI, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UIModule, Version=0.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-1.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-10.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-11.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-12.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-2.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-3.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-4.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-5.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-6.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-7.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-8.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\cat-9.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\rainbow2.png" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>sh -c "TARGET_PATH='$(TargetPath)' TARGET_DIR='$(TargetDir)' TARGET_NAME='$(TargetName)' sh '$(ProjectDir)/copy_build.sh'"</PostBuildEvent>
</PropertyGroup>
</Project>
================================================
FILE: ModuleManager/ModuleManagerTestRunner.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ModuleManager.Extensions;
using ModuleManager.Logging;
namespace ModuleManager
{
public class InGameTestRunner
{
private readonly IBasicLogger logger;
public InGameTestRunner(IBasicLogger logger)
{
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public void RunTestCases(UrlDir gameDatabaseRoot)
{
if (gameDatabaseRoot == null) throw new ArgumentNullException(nameof(gameDatabaseRoot));
logger.Info("Running tests...");
foreach (UrlDir.UrlConfig expect in gameDatabaseRoot.GetConfigs("MMTEST_EXPECT"))
{
// So for each of the expects, we expect all the configs before that node to match exactly.
UrlDir.UrlFile parent = expect.parent;
if (parent.configs.Count != expect.config.CountNodes + 1)
{
logger.Error("Test " + parent.name + " failed as expected number of nodes differs expected: " +
expect.config.CountNodes + " found: " + (parent.configs.Count - 1));
for (int i = 0; i < parent.configs.Count; ++i)
logger.Info(parent.configs[i].config.ToString());
continue;
}
for (int i = 0; i < expect.config.CountNodes; ++i)
{
ConfigNode gotNode = parent.configs[i].config;
ConfigNode expectNode = expect.config.nodes[i];
if (!CompareRecursive(expectNode, gotNode))
{
logger.Error("Test " + parent.name + "[" + i +
"] failed as expected output and actual output differ.\nexpected:\n" + expectNode +
"\nActually got:\n" + gotNode);
}
}
// Purge the tests
parent.configs.Clear();
}
logger.Info("tests complete.");
}
private static bool CompareRecursive(ConfigNode expectNode, ConfigNode gotNode)
{
if (expectNode.values.Count != gotNode.values.Count || expectNode.nodes.Count != gotNode.nodes.Count)
return false;
for (int i = 0; i < expectNode.values.Count; ++i)
{
ConfigNode.Value eVal = expectNode.values[i];
ConfigNode.Value gVal = gotNode.values[i];
if (eVal.name != gVal.name || eVal.value != gVal.value)
return false;
}
for (int i = 0; i < expectNode.nodes.Count; ++i)
{
ConfigNode eNode = expectNode.nodes[i];
ConfigNode gNode = gotNode.nodes[i];
if (!CompareRecursive(eNode, gNode))
return false;
}
return true;
}
}
}
================================================
FILE: ModuleManager/NeedsChecker.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using ModuleManager.Extensions;
using ModuleManager.Logging;
using ModuleManager.Progress;
using NodeStack = ModuleManager.Collections.ImmutableStack<ConfigNode>;
namespace ModuleManager
{
public interface INeedsChecker
{
bool CheckNeeds(string mod);
bool CheckNeedsExpression(string needsString);
void CheckNeedsRecursive(ConfigNode node, UrlDir.UrlConfig urlConfig);
}
public class NeedsChecker : INeedsChecker
{
private readonly IEnumerable<string> mods;
private readonly UrlDir gameData;
private readonly IPatchProgress progress;
[SuppressMessage("CodeQuality", "IDE0052", Justification = "Reserved for future use")]
private readonly IBasicLogger logger;
public NeedsChecker(IEnumerable<string> mods, UrlDir gameData, IPatchProgress progress, IBasicLogger logger)
{
this.mods = mods ?? throw new ArgumentNullException(nameof(mods));
this.gameData = gameData ?? throw new ArgumentNullException(nameof(gameData));
this.progress = progress ?? throw new ArgumentNullException(nameof(progress));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public bool CheckNeeds(string mod)
{
if (mod == null) throw new ArgumentNullException(nameof(mod));
if (mod == string.Empty) throw new ArgumentException("can't be empty", nameof(mod));
return mods.Contains(mod, StringComparer.InvariantCultureIgnoreCase);
}
public bool CheckNeedsExpression(string needsExpression)
{
if (needsExpression == null) throw new ArgumentNullException(nameof(needsExpression));
if (needsExpression == string.Empty) throw new ArgumentException("can't be empty", nameof(needsExpression));
foreach (string andDependencies in needsExpression.Split(',', '&'))
{
bool orMatch = false;
foreach (string orDependency in andDependencies.Split('|'))
{
if (orDependency.Length == 0)
continue;
bool not = orDependency[0] == '!';
string toFind = not ? orDependency.Substring(1) : orDependency;
bool found = CheckNeedsWithDirectories(toFind);
if (not == !found)
{
orMatch = true;
break;
}
}
if (!orMatch)
return false;
}
return true;
}
public void CheckNeedsRecursive(ConfigNode node, UrlDir.UrlConfig urlConfig)
{
if (node == null) throw new ArgumentNullException(nameof(node));
if (urlConfig == null) throw new ArgumentNullException(nameof(urlConfig));
CheckNeedsRecursive(new NodeStack(node), urlConfig);
}
private bool CheckNeedsWithDirectories(string mod)
{
if (CheckNeeds(mod)) return true;
if (mod.Contains('/'))
{
string[] splits = mod.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
bool result = true;
UrlDir current = gameData;
for (int i = 0; i < splits.Length; i++)
{
current = current.children.FirstOrDefault(dir => dir.name == splits[i]);
if (current == null)
{
result = false;
break;
}
}
return result;
}
return false;
}
private bool CheckNeedsName(ref string name)
{
if (name == null)
return true;
int idxStart = name.IndexOf(":NEEDS[", StringComparison.OrdinalIgnoreCase);
if (idxStart < 0)
return true;
int idxEnd = name.IndexOf(']', idxStart + 7);
string needsString = name.Substring(idxStart + 7, idxEnd - idxStart - 7);
name = name.Substring(0, idxStart) + name.Substring(idxEnd + 1);
return CheckNeedsExpression(needsString);
}
private void CheckNeedsRecursive(NodeStack nodeStack, UrlDir.UrlConfig urlConfig)
{
ConfigNode original = nodeStack.value;
for (int i = 0; i < original.values.Count; ++i)
{
ConfigNode.Value val = original.values[i];
string valname = val.name;
try
{
if (CheckNeedsName(ref valname))
{
val.name = valname;
}
else
{
original.values.Remove(val);
i--;
progress.NeedsUnsatisfiedValue(urlConfig, nodeStack.GetPath() + '/' + val.name);
}
}
catch (ArgumentOutOfRangeException e)
{
progress.Exception("ArgumentOutOfRangeException in CheckNeeds for value \"" + val.name + "\"", e);
throw;
}
catch (Exception e)
{
progress.Exception("General Exception in CheckNeeds for value \"" + val.name + "\"", e);
throw;
}
}
for (int i = 0; i < original.nodes.Count; ++i)
{
ConfigNode node = original.nodes[i];
string nodeName = node.name;
if (nodeName == null)
{
progress.Error(urlConfig, "Error - Node in file " + urlConfig.SafeUrl() + " subnode: " + nodeStack.GetPath() + " has config.name == null");
}
try
{
if (CheckNeedsName(ref nodeName))
{
node.name = nodeName;
CheckNeedsRecursive(nodeStack.Push(node), urlConfig);
}
else
{
original.nodes.Remove(node);
i--;
progress.NeedsUnsatisfiedNode(urlConfig, nodeStack.GetPath() + '/' + node.name);
}
}
catch (ArgumentOutOfRangeException e)
{
progress.Exception("ArgumentOutOfRangeException in CheckNeeds for node \"" + node.name + "\"", e);
throw;
}
catch (Exception e)
{
progress.Exception("General Exception " + e.GetType().Name + " for node \"" + node.name + "\"", e);
throw;
}
}
}
}
}
================================================
FILE: ModuleManager/NodeMatcher.cs
================================================
using System;
using ModuleManager.Extensions;
namespace ModuleManager
{
public interface INodeMatcher
{
bool IsMatch(ConfigNode node);
}
public class NodeMatcher : INodeMatcher
{
private readonly string type;
private readonly string[] namePatterns = null;
private readonly string constraints = "";
public NodeMatcher(string type, string name, string constraints)
{
if (type == string.Empty) throw new ArgumentException("can't be empty", nameof(type));
this.type = type ?? throw new ArgumentNullException(nameof(type));
if (name == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(name));
if (constraints == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(constraints));
if (name != null) namePatterns = name.Split(',', '|');
if (constraints != null)
{
if (!constraints.IsBracketBalanced()) throw new ArgumentException("is not bracket balanced: " + constraints, nameof(constraints));
this.constraints = constraints;
}
}
public bool IsMatch(ConfigNode node)
{
if (node.name != type) return false;
if (namePatterns != null)
{
string name = node.GetValue("name");
if (name == null) return false;
bool match = false;
foreach (string pattern in namePatterns)
{
if (MMPatchLoader.WildcardMatch(name, pattern))
{
match = true;
break;
}
}
if (!match) return false;
}
return MMPatchLoader.CheckConstraints(node, constraints);
}
}
}
================================================
FILE: ModuleManager/Operator.cs
================================================
using System;
namespace ModuleManager
{
public enum Operator
{
Assign,
Add,
Subtract,
Multiply,
Divide,
Exponentiate,
RegexReplace,
}
}
================================================
FILE: ModuleManager/OperatorParser.cs
================================================
using System;
namespace ModuleManager
{
public static class OperatorParser
{
public static Operator Parse(string name, out string valueName)
{
if (name == null) throw new ArgumentNullException(nameof(name));
if (name.Length == 0)
{
valueName = string.Empty;
return Operator.Assign;
}
else if (name.Length == 1 || (name[name.Length - 2] != ' ' && name[name.Length - 2] != '\t'))
{
valueName = name;
return Operator.Assign;
}
Operator ret;
switch (name[name.Length - 1])
{
case '+':
ret = Operator.Add;
break;
case '-':
ret = Operator.Subtract;
break;
case '*':
ret = Operator.Multiply;
break;
case '/':
ret = Operator.Divide;
break;
case '!':
ret = Operator.Exponentiate;
break;
case '^':
ret = Operator.RegexReplace;
break;
default:
valueName = name;
return Operator.Assign;
}
valueName = name.Substring(0, name.Length - 1).TrimEnd();
return ret;
}
}
}
================================================
FILE: ModuleManager/Pass.cs
================================================
using System;
using System.Collections;
using System.Collections.Generic;
using ModuleManager.Patches;
namespace ModuleManager
{
public interface IPass : IEnumerable<IPatch>
{
string Name { get; }
}
public class Pass : IPass
{
private readonly string name;
private readonly List<IPatch> patches = new List<IPatch>(0);
public Pass(string name)
{
this.name = name ?? throw new ArgumentNullException(nameof(name));
if (name == string.Empty) throw new ArgumentException("can't be empty", nameof(name));
}
public string Name => name;
public void Add(IPatch patch) => patches.Add(patch);
public List<IPatch>.Enumerator GetEnumerator() => patches.GetEnumerator();
IEnumerator<IPatch> IEnumerable<IPatch>.GetEnumerator() => GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}
}
================================================
FILE: ModuleManager/PatchApplier.cs
================================================
using System;
using System.Collections.Generic;
using ModuleManager.Logging;
using ModuleManager.Extensions;
using ModuleManager.Patches;
using ModuleManager.Progress;
namespace ModuleManager
{
public class PatchApplier
{
private readonly IBasicLogger logger;
private readonly IPatchProgress progress;
public PatchApplier(IPatchProgress progress, IBasicLogger logger)
{
this.progress = progress ?? throw new ArgumentNullException(nameof(progress));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public IEnumerable<IProtoUrlConfig> ApplyPatches(IEnumerable<IPass> patches)
{
if (patches == null) throw new ArgumentNullException(nameof(patches));
LinkedList<IProtoUrlConfig> databaseConfigs = new LinkedList<IProtoUrlConfig>();
foreach (IPass pass in patches)
{
ApplyPatches(databaseConfigs, pass);
}
return databaseConfigs;
}
private void ApplyPatches(LinkedList<IProtoUrlConfig> databaseConfigs, IPass pass)
{
progress.PassStarted(pass);
foreach (IPatch patch in pass)
{
try
{
patch.Apply(databaseConfigs, progress, logger);
if (patch.CountsAsPatch) progress.PatchApplied();
}
catch (Exception e)
{
progress.Exception(patch.UrlConfig, "Exception while processing node : " + patch.UrlConfig.SafeUrl(), e);
logger.Error("Processed node was\n" + patch.UrlConfig.PrettyPrint());
}
}
}
}
}
================================================
FILE: ModuleManager/PatchContext.cs
================================================
using System;
using System.Collections.Generic;
using ModuleManager.Logging;
using ModuleManager.Progress;
namespace ModuleManager
{
public struct PatchContext
{
public readonly UrlDir.UrlConfig patchUrl;
public readonly IEnumerable<IProtoUrlConfig> databaseConfigs;
public readonly IBasicLogger logger;
public readonly IPatchProgress progress;
public PatchContext(UrlDir.UrlConfig patchUrl, IEnumerable<IProtoUrlConfig> databaseConfigs, IBasicLogger logger, IPatchProgress progress)
{
this.patchUrl = patchUrl;
this.databaseConfigs = databaseConfigs;
this.logger = logger;
this.progress = progress;
}
}
}
================================================
FILE: ModuleManager/PatchExtractor.cs
================================================
using System;
using System.Diagnostics.CodeAnalysis;
using ModuleManager.Extensions;
using ModuleManager.Logging;
using ModuleManager.Patches;
using ModuleManager.Progress;
using ModuleManager.Tags;
namespace ModuleManager
{
public class PatchExtractor
{
private readonly IPatchProgress progress;
[SuppressMessage("CodeQuality", "IDE0052", Justification = "Reserved for future use")]
private readonly IBasicLogger logger;
private readonly INeedsChecker needsChecker;
private readonly ITagListParser tagListParser;
private readonly IProtoPatchBuilder protoPatchBuilder;
private readonly IPatchCompiler patchCompiler;
public PatchExtractor(
IPatchProgress progress,
IBasicLogger logger,
INeedsChecker needsChecker,
ITagListParser tagListParser,
IProtoPatchBuilder protoPatchBuilder,
IPatchCompiler patchCompiler
)
{
this.progress = progress ?? throw new ArgumentNullException(nameof(progress));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
this.needsChecker = needsChecker ?? throw new ArgumentNullException(nameof(needsChecker));
this.tagListParser = tagListParser ?? throw new ArgumentNullException(nameof(tagListParser));
this.protoPatchBuilder = protoPatchBuilder ?? throw new ArgumentNullException(nameof(protoPatchBuilder));
this.patchCompiler = patchCompiler ?? throw new ArgumentNullException(nameof(patchCompiler));
}
public IPatch ExtractPatch(UrlDir.UrlConfig urlConfig)
{
if (urlConfig == null) throw new ArgumentNullException(nameof(urlConfig));
try
{
if (!urlConfig.type.IsBracketBalanced())
{
progress.Error(urlConfig, "Error - node name does not have balanced brackets (or a space - if so replace with ?):\n" + urlConfig.SafeUrl());
return null;
}
Command command = CommandParser.Parse(urlConfig.type, out string name);
if (command == Command.Replace)
{
progress.Error(urlConfig, $"Error - replace command (%) is not valid on a root node: {urlConfig.SafeUrl()}");
return null;
}
else if (command == Command.Create)
{
progress.Error(urlConfig, $"Error - create command (&) is not valid on a root node: {urlConfig.SafeUrl()}");
return null;
}
else if (command == Command.Rename)
{
progress.Error(urlConfig, $"Error - rename command (|) is not valid on a root node: {urlConfig.SafeUrl()}");
return null;
}
else if (command == C
gitextract_882ubem8/
├── .gitignore
├── CopyLocalFalse.txt
├── Makefile
├── 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
│ ├── 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
├── ModuleManager.sln
├── ModuleManager.sln.DotSettings
├── 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
SYMBOL INDEX (959 symbols across 136 files)
FILE: ModuleManager/Cats/CatAnimator.cs
class CatAnimator (line 7) | class CatAnimator : MonoBehaviour
method Start (line 16) | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by ...
method Animate (line 25) | IEnumerator Animate()
FILE: ModuleManager/Cats/CatManager.cs
class CatManager (line 6) | public static class CatManager
method LaunchCat (line 12) | public static void LaunchCat()
method LaunchCats (line 20) | public static void LaunchCats()
method InitCats (line 46) | private static void InitCats()
method LaunchCat (line 88) | private static GameObject LaunchCat(int scale)
FILE: ModuleManager/Cats/CatMover.cs
class CatMover (line 6) | public class CatMover : MonoBehaviour
method Start (line 27) | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by ...
method Update (line 45) | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by ...
FILE: ModuleManager/Cats/CatOrbiter.cs
class CatOrbiter (line 10) | class CatOrbiter : MonoBehaviour
method Init (line 37) | public void Init(CatOrbiter parent, float soi)
method DoForces (line 102) | private void DoForces()
method OnDestroy (line 117) | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by ...
method FixedUpdate (line 124) | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by ...
FILE: ModuleManager/Collections/ArrayEnumerator.cs
type ArrayEnumerator (line 7) | public struct ArrayEnumerator<T> : IEnumerator<T>
method ArrayEnumerator (line 15) | public ArrayEnumerator(params T[] array) : this(array, 0) { }
method ArrayEnumerator (line 17) | public ArrayEnumerator(T[] array, int startIndex) : this(array, startI...
method ArrayEnumerator (line 19) | public ArrayEnumerator(T[] array, int startIndex, int length)
method Dispose (line 46) | public void Dispose() { }
method MoveNext (line 48) | public bool MoveNext()
method Reset (line 54) | public void Reset()
FILE: ModuleManager/Collections/ImmutableStack.cs
class ImmutableStack (line 7) | public class ImmutableStack<T> : IEnumerable<T>
type Enumerator (line 9) | public struct Enumerator : IEnumerator<T>
method Enumerator (line 14) | public Enumerator(ImmutableStack<T> stack)
method Dispose (line 23) | public void Dispose() { }
method MoveNext (line 25) | public bool MoveNext()
method Reset (line 43) | public void Reset() => currentStack = null;
method ImmutableStack (line 49) | public ImmutableStack(T value)
method ImmutableStack (line 54) | private ImmutableStack(T value, ImmutableStack<T> parent)
method Push (line 65) | public ImmutableStack<T> Push(T newValue)
method Pop (line 70) | public ImmutableStack<T> Pop()
method ReplaceValue (line 76) | public ImmutableStack<T> ReplaceValue(T newValue) => new ImmutableStac...
method GetEnumerator (line 78) | public Enumerator GetEnumerator() => new Enumerator(this);
method GetEnumerator (line 79) | IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
method GetEnumerator (line 80) | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
FILE: ModuleManager/Collections/KeyValueCache.cs
class KeyValueCache (line 6) | public class KeyValueCache<TKey, TValue>
method Fetch (line 11) | public TValue Fetch(TKey key, Func<TValue> createValue)
FILE: ModuleManager/Collections/MessageQueue.cs
type IMessageQueue (line 7) | public interface IMessageQueue<T> : IEnumerable<T>
method Add (line 9) | void Add(T value);
method TakeAll (line 10) | IMessageQueue<T> TakeAll();
class MessageQueue (line 13) | public class MessageQueue<T> : IMessageQueue<T>, IEnumerable<T>
class Enumerator (line 15) | public sealed class Enumerator : IEnumerator<T>
method Enumerator (line 20) | public Enumerator(MessageQueue<T> queue)
method Dispose (line 28) | public void Dispose() { }
method MoveNext (line 30) | public bool MoveNext()
method Reset (line 40) | public void Reset()
class Node (line 46) | private class Node
method Node (line 51) | public Node(T value)
method Add (line 61) | public void Add(T value)
method TakeAll (line 79) | public IMessageQueue<T> TakeAll()
method GetEnumerator (line 92) | public Enumerator GetEnumerator() => new Enumerator(this);
method GetEnumerator (line 93) | IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
method GetEnumerator (line 94) | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
FILE: ModuleManager/Command.cs
type Command (line 5) | public enum Command
FILE: ModuleManager/CommandParser.cs
class CommandParser (line 5) | public static class CommandParser
method Parse (line 7) | public static Command Parse(string name, out string valueName)
FILE: ModuleManager/CustomConfigsManager.cs
class CustomConfigsManager (line 9) | [KSPAddon(KSPAddon.Startup.SpaceCentre, false)]
method Start (line 12) | internal void Start()
method Log (line 21) | public static void Log(String s)
FILE: ModuleManager/ExceptionIntercept/InterceptLogHandler.cs
class InterceptLogHandler (line 11) | class InterceptLogHandler : ILogHandler
method InterceptLogHandler (line 19) | public InterceptLogHandler(ILogHandler baseLogHandler)
method LogFormat (line 25) | public void LogFormat(LogType logType, Object context, string format, ...
method LogException (line 30) | public void LogException(Exception exception, Object context)
FILE: ModuleManager/Extensions/ByteArrayExtensions.cs
class ByteArrayExtensions (line 5) | public static class ByteArrayExtensions
method ToHex (line 7) | public static string ToHex(this byte[] data)
method GetHexValue (line 21) | private static char GetHexValue(int i)
FILE: ModuleManager/Extensions/ConfigNodeExtensions.cs
class ConfigNodeExtensions (line 6) | public static class ConfigNodeExtensions
method ShallowCopyFrom (line 8) | public static void ShallowCopyFrom(this ConfigNode toNode, ConfigNode ...
method DeepCopy (line 18) | public static ConfigNode DeepCopy(this ConfigNode from)
method PrettyPrint (line 31) | public static void PrettyPrint(this ConfigNode node, ref StringBuilder...
method AddValueSafe (line 72) | public static void AddValueSafe(this ConfigNode node, string name, str...
method EscapeValuesRecursive (line 77) | public static void EscapeValuesRecursive(this ConfigNode theNode)
method UnescapeValuesRecursive (line 92) | public static void UnescapeValuesRecursive(this ConfigNode theNode)
FILE: ModuleManager/Extensions/IBasicLoggerExtensions.cs
class IBasicLoggerExtensions (line 7) | public static class IBasicLoggerExtensions
method Info (line 9) | public static void Info(this IBasicLogger logger, string message) => l...
method Warning (line 10) | public static void Warning(this IBasicLogger logger, string message) =...
method Error (line 11) | public static void Error(this IBasicLogger logger, string message) => ...
method Exception (line 13) | public static void Exception(this IBasicLogger logger, Exception excep...
method Exception (line 19) | public static void Exception(this IBasicLogger logger, string message,...
FILE: ModuleManager/Extensions/NodeStackExtensions.cs
class NodeStackExtensions (line 8) | public static class NodeStackExtensions
method GetPath (line 10) | public static string GetPath(this NodeStack stack)
FILE: ModuleManager/Extensions/StringExtensions.cs
class StringExtensions (line 6) | public static class StringExtensions
method IsBracketBalanced (line 8) | public static bool IsBracketBalanced(this string s)
method RemoveWS (line 23) | public static string RemoveWS(this string withWhite)
method Contains (line 28) | public static bool Contains(this string str, string value, out int index)
FILE: ModuleManager/Extensions/UrlConfigExtensions.cs
class UrlConfigExtensions (line 6) | public static class UrlConfigExtensions
method SafeUrl (line 8) | public static string SafeUrl(this UrlDir.UrlConfig url)
method PrettyPrint (line 47) | public static string PrettyPrint(this UrlDir.UrlConfig config)
FILE: ModuleManager/Extensions/UrlDirExtensions.cs
class UrlDirExtensions (line 6) | public static class UrlDirExtensions
method Find (line 8) | public static UrlDir.UrlFile Find(this UrlDir urlDir, string url)
FILE: ModuleManager/Extensions/UrlFileExtensions.cs
class UrlFileExtensions (line 5) | public static class UrlFileExtensions
method GetUrlWithExtension (line 7) | public static string GetUrlWithExtension(this UrlDir.UrlFile urlFile)
method GetNameWithExtension (line 11) | public static string GetNameWithExtension(this UrlDir.UrlFile urlFile)
FILE: ModuleManager/FatalErrorHandler.cs
class FatalErrorHandler (line 6) | public static class FatalErrorHandler
method HandleFatalError (line 8) | public static void HandleFatalError(string message)
FILE: ModuleManager/FilePathRepository.cs
class FilePathRepository (line 6) | internal static class FilePathRepository
FILE: ModuleManager/Fix16.cs
class Fix16 (line 6) | class Fix16 : LoadingSystem
method Awake (line 8) | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by ...
method IsReady (line 31) | public override bool IsReady()
method ProgressTitle (line 36) | public override string ProgressTitle()
method ProgressFraction (line 41) | public override float ProgressFraction()
method StartLoad (line 46) | public override void StartLoad()
method DoFix (line 55) | private IEnumerator DoFix()
method LoadWeight (line 78) | public override float LoadWeight()
FILE: ModuleManager/Logging/IBasicLogger.cs
type IBasicLogger (line 6) | public interface IBasicLogger
method Log (line 8) | void Log(ILogMessage message);
FILE: ModuleManager/Logging/ILogMessage.cs
type ILogMessage (line 6) | public interface ILogMessage
method ToLogString (line 11) | string ToLogString();
FILE: ModuleManager/Logging/LogMessage.cs
class LogMessage (line 6) | public class LogMessage : ILogMessage
method LogMessage (line 14) | public LogMessage(LogType logType, string message)
method LogMessage (line 21) | public LogMessage(ILogMessage logMessage, string newMessage)
method ToLogString (line 29) | public string ToLogString()
method ToString (line 48) | public override string ToString()
FILE: ModuleManager/Logging/LogSplitter.cs
class LogSplitter (line 5) | public class LogSplitter : IBasicLogger
method LogSplitter (line 10) | public LogSplitter(IBasicLogger logger1, IBasicLogger logger2)
method Log (line 16) | public void Log(ILogMessage message)
FILE: ModuleManager/Logging/PrefixLogger.cs
class PrefixLogger (line 5) | public class PrefixLogger : IBasicLogger
method PrefixLogger (line 10) | public PrefixLogger(string prefix, IBasicLogger logger)
method Log (line 17) | public void Log(ILogMessage message)
FILE: ModuleManager/Logging/QueueLogRunner.cs
class QueueLogRunner (line 8) | public class QueueLogRunner
type State (line 10) | private enum State
method QueueLogRunner (line 22) | public QueueLogRunner(IMessageQueue<ILogMessage> logQueue, long timeTo...
method RequestStop (line 29) | public void RequestStop()
method Run (line 36) | public void Run(IBasicLogger logger)
FILE: ModuleManager/Logging/QueueLogger.cs
class QueueLogger (line 6) | public class QueueLogger : IBasicLogger
method QueueLogger (line 10) | public QueueLogger(IMessageQueue<ILogMessage> queue)
method Log (line 15) | public void Log(ILogMessage message)
FILE: ModuleManager/Logging/StreamLogger.cs
class StreamLogger (line 6) | public sealed class StreamLogger : IBasicLogger, IDisposable
method StreamLogger (line 11) | public StreamLogger(Stream stream)
method Log (line 16) | public void Log(ILogMessage message)
method Dispose (line 24) | public void Dispose()
FILE: ModuleManager/Logging/UnityLogger.cs
class UnityLogger (line 6) | public class UnityLogger : IBasicLogger
method UnityLogger (line 10) | public UnityLogger(ILogger logger)
method Log (line 15) | public void Log(ILogMessage message)
FILE: ModuleManager/MMPatchLoader.cs
class MMPatchLoader (line 25) | public class MMPatchLoader
method AddPostPatchCallback (line 46) | public static void AddPostPatchCallback(ModuleManagerPostPatchCallback...
method MMPatchLoader (line 51) | public MMPatchLoader(IEnumerable<ModListGenerator.ModAddedByAssembly> ...
method Run (line 57) | public IEnumerable<IProtoUrlConfig> Run()
method LoadPhysicsConfig (line 245) | private void LoadPhysicsConfig()
method SaveModdedPhysics (line 260) | private void SaveModdedPhysics(IEnumerable<IProtoUrlConfig> databaseCo...
method IsCacheUpToDate (line 279) | private bool IsCacheUpToDate()
method CheckFilesChange (line 362) | private bool CheckFilesChange(UrlDir.UrlFile[] files, ConfigNode shaCo...
method GetFileNode (line 406) | private ConfigNode GetFileNode(ConfigNode shaConfigNode, string filename)
method CreateCache (line 418) | private void CreateCache(IEnumerable<IProtoUrlConfig> databaseConfigs,...
method SaveModdedTechTree (line 492) | private void SaveModdedTechTree(IEnumerable<IProtoUrlConfig> databaseC...
method LoadCache (line 513) | private IEnumerable<IProtoUrlConfig> LoadCache()
method StatusUpdate (line 548) | private void StatusUpdate(IPatchProgress progress, string activity = n...
method ModifyNode (line 574) | public static ConfigNode ModifyNode(NodeStack original, ConfigNode mod...
method RecurseNodeSearch (line 1102) | private static ConfigNode RecurseNodeSearch(string path, NodeStack nod...
method RecurseVariableSearch (line 1222) | private static ConfigNode.Value RecurseVariableSearch(string path, Nod...
method ProcessVariableSearch (line 1383) | private static string ProcessVariableSearch(string value, NodeStack no...
method FindAndReplaceValue (line 1412) | private static string FindAndReplaceValue(
method SplitConstraints (line 1515) | public static List<string> SplitConstraints(string condition)
method CheckConstraints (line 1538) | public static bool CheckConstraints(ConfigNode node, string constraints)
method WildcardMatchValues (line 1635) | public static bool WildcardMatchValues(ConfigNode node, string type, s...
method WildcardMatch (line 1656) | public static bool WildcardMatch(string s, string wildcard)
method InsertNode (line 1673) | private static void InsertNode(ConfigNode newNode, ConfigNode subMod, ...
method InsertValue (line 1692) | private static void InsertValue(ConfigNode newNode, int index, string ...
method FindConfigNodeIn (line 1712) | public static ConfigNode FindConfigNodeIn(
method FindValueIn (line 1760) | private static ConfigNode.Value FindValueIn(ConfigNode newNode, string...
FILE: ModuleManager/MMPatchRunner.cs
class MMPatchRunner (line 14) | public class MMPatchRunner
method MMPatchRunner (line 21) | public MMPatchRunner(IBasicLogger kspLogger)
method Run (line 26) | public IEnumerator Run()
FILE: ModuleManager/ModListGenerator.cs
class ModListGenerator (line 15) | public static class ModListGenerator
method GenerateModList (line 17) | public static IEnumerable<string> GenerateModList(IEnumerable<ModAdded...
class ModAddedByAssembly (line 150) | public class ModAddedByAssembly
method ModAddedByAssembly (line 155) | public ModAddedByAssembly(string modName, string assemblyName)
method ToString (line 161) | public override string ToString()
method GetAdditionalModsFromStaticMethods (line 167) | public static IEnumerable<ModAddedByAssembly> GetAdditionalModsFromSta...
FILE: ModuleManager/ModuleManager.cs
class ModuleManager (line 19) | [KSPAddon(KSPAddon.Startup.Instantly, true)]
method OnRnDCenterSpawn (line 45) | internal void OnRnDCenterSpawn()
method OnRnDCenterDeSpawn (line 50) | internal void OnRnDCenterDeSpawn()
method Log (line 55) | public static void Log(String s)
method Awake (line 62) | internal void Awake()
method Start (line 163) | [SuppressMessage("Code Quality", "IDE0051", Justification = "Called by...
method CreateTextObject (line 198) | private TextMeshProUGUI CreateTextObject(Canvas canvas, string name)
method OnDestroy (line 218) | internal void OnDestroy()
method Update (line 224) | internal void Update()
method DataBaseReloadWithMM (line 323) | private IEnumerator DataBaseReloadWithMM(bool dump = false)
method OutputAllConfigs (line 441) | public static void OutputAllConfigs()
method ElectionAndCheck (line 524) | public bool ElectionAndCheck()
FILE: ModuleManager/ModuleManagerTestRunner.cs
class InGameTestRunner (line 10) | public class InGameTestRunner
method InGameTestRunner (line 14) | public InGameTestRunner(IBasicLogger logger)
method RunTestCases (line 19) | public void RunTestCases(UrlDir gameDatabaseRoot)
method CompareRecursive (line 54) | private static bool CompareRecursive(ConfigNode expectNode, ConfigNode...
FILE: ModuleManager/NeedsChecker.cs
type INeedsChecker (line 12) | public interface INeedsChecker
method CheckNeeds (line 14) | bool CheckNeeds(string mod);
method CheckNeedsExpression (line 15) | bool CheckNeedsExpression(string needsString);
method CheckNeedsRecursive (line 16) | void CheckNeedsRecursive(ConfigNode node, UrlDir.UrlConfig urlConfig);
class NeedsChecker (line 19) | public class NeedsChecker : INeedsChecker
method NeedsChecker (line 27) | public NeedsChecker(IEnumerable<string> mods, UrlDir gameData, IPatchP...
method CheckNeeds (line 35) | public bool CheckNeeds(string mod)
method CheckNeedsExpression (line 42) | public bool CheckNeedsExpression(string needsExpression)
method CheckNeedsRecursive (line 73) | public void CheckNeedsRecursive(ConfigNode node, UrlDir.UrlConfig urlC...
method CheckNeedsWithDirectories (line 80) | private bool CheckNeedsWithDirectories(string mod)
method CheckNeedsName (line 103) | private bool CheckNeedsName(ref string name)
method CheckNeedsRecursive (line 119) | private void CheckNeedsRecursive(NodeStack nodeStack, UrlDir.UrlConfig...
FILE: ModuleManager/NodeMatcher.cs
type INodeMatcher (line 6) | public interface INodeMatcher
method IsMatch (line 8) | bool IsMatch(ConfigNode node);
class NodeMatcher (line 11) | public class NodeMatcher : INodeMatcher
method NodeMatcher (line 17) | public NodeMatcher(string type, string name, string constraints)
method IsMatch (line 33) | public bool IsMatch(ConfigNode node)
FILE: ModuleManager/Operator.cs
type Operator (line 5) | public enum Operator
FILE: ModuleManager/OperatorParser.cs
class OperatorParser (line 5) | public static class OperatorParser
method Parse (line 7) | public static Operator Parse(string name, out string valueName)
FILE: ModuleManager/Pass.cs
type IPass (line 8) | public interface IPass : IEnumerable<IPatch>
class Pass (line 13) | public class Pass : IPass
method Pass (line 18) | public Pass(string name)
method Add (line 26) | public void Add(IPatch patch) => patches.Add(patch);
method GetEnumerator (line 28) | public List<IPatch>.Enumerator GetEnumerator() => patches.GetEnumerato...
method GetEnumerator (line 29) | IEnumerator<IPatch> IEnumerable<IPatch>.GetEnumerator() => GetEnumerat...
method GetEnumerator (line 30) | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
FILE: ModuleManager/PatchApplier.cs
class PatchApplier (line 10) | public class PatchApplier
method PatchApplier (line 15) | public PatchApplier(IPatchProgress progress, IBasicLogger logger)
method ApplyPatches (line 21) | public IEnumerable<IProtoUrlConfig> ApplyPatches(IEnumerable<IPass> pa...
method ApplyPatches (line 35) | private void ApplyPatches(LinkedList<IProtoUrlConfig> databaseConfigs,...
FILE: ModuleManager/PatchContext.cs
type PatchContext (line 8) | public struct PatchContext
method PatchContext (line 15) | public PatchContext(UrlDir.UrlConfig patchUrl, IEnumerable<IProtoUrlCo...
FILE: ModuleManager/PatchExtractor.cs
class PatchExtractor (line 11) | public class PatchExtractor
method PatchExtractor (line 21) | public PatchExtractor(
method ExtractPatch (line 38) | public IPatch ExtractPatch(UrlDir.UrlConfig urlConfig)
FILE: ModuleManager/PatchList.cs
class PatchList (line 10) | public class PatchList : IEnumerable<IPass>
class ModPass (line 12) | private class ModPass
method ModPass (line 20) | public ModPass(string name)
method AddBeforePatch (line 32) | public void AddBeforePatch(IPatch patch) => beforePass.Add(patch ?? ...
method AddForPatch (line 33) | public void AddForPatch(IPatch patch) => forPass.Add(patch ?? throw ...
method AddAfterPatch (line 34) | public void AddAfterPatch(IPatch patch) => afterPass.Add(patch ?? th...
method AddLastPatch (line 35) | public void AddLastPatch(IPatch patch) => lastPass.Add(patch ?? thro...
method PatchList (line 46) | public PatchList(IEnumerable<string> modList, IEnumerable<IPatch> patc...
method GetEnumerator (line 108) | public IEnumerator<IPass> GetEnumerator()
method GetEnumerator (line 129) | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
method EnsureMod (line 131) | private void EnsureMod(string mod)
FILE: ModuleManager/Patches/CopyPatch.cs
class CopyPatch (line 11) | public class CopyPatch : IPatch
method CopyPatch (line 18) | public CopyPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher,...
method Apply (line 25) | public void Apply(LinkedList<IProtoUrlConfig> databaseConfigs, IPatchP...
FILE: ModuleManager/Patches/DeletePatch.cs
class DeletePatch (line 10) | public class DeletePatch : IPatch
method DeletePatch (line 17) | public DeletePatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatche...
method Apply (line 24) | public void Apply(LinkedList<IProtoUrlConfig> databaseConfigs, IPatchP...
FILE: ModuleManager/Patches/EditPatch.cs
class EditPatch (line 11) | public class EditPatch : IPatch
method EditPatch (line 20) | public EditPatch(UrlDir.UrlConfig urlConfig, INodeMatcher nodeMatcher,...
method Apply (line 29) | public void Apply(LinkedList<IProtoUrlConfig> databaseConfigs, IPatchP...
FILE: ModuleManager/Patches/IPatch.cs
type IPatch (line 9) | public interface IPatch
method Apply (line 14) | void Apply(LinkedList<IProtoUrlConfig> configs, IPatchProgress progres...
FILE: ModuleManager/Patches/InsertPatch.cs
class InsertPatch (line 10) | public class InsertPatch : IPatch
method InsertPatch (line 17) | public InsertPatch(UrlDir.UrlConfig urlConfig, string nodeType, IPassS...
method Apply (line 24) | public void Apply(LinkedList<IProtoUrlConfig> configs, IPatchProgress ...
FILE: ModuleManager/Patches/PassSpecifiers/AfterPassSpecifier.cs
class AfterPassSpecifier (line 6) | public class AfterPassSpecifier : IPassSpecifier
method AfterPassSpecifier (line 11) | public AfterPassSpecifier(string mod, UrlDir.UrlConfig urlConfig)
method CheckNeeds (line 18) | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress prog...
FILE: ModuleManager/Patches/PassSpecifiers/BeforePassSpecifier.cs
class BeforePassSpecifier (line 6) | public class BeforePassSpecifier : IPassSpecifier
method BeforePassSpecifier (line 11) | public BeforePassSpecifier(string mod, UrlDir.UrlConfig urlConfig)
method CheckNeeds (line 18) | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress prog...
FILE: ModuleManager/Patches/PassSpecifiers/FinalPassSpecifier.cs
class FinalPassSpecifier (line 6) | public class FinalPassSpecifier : IPassSpecifier
method CheckNeeds (line 8) | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress prog...
FILE: ModuleManager/Patches/PassSpecifiers/FirstPassSpecifier.cs
class FirstPassSpecifier (line 6) | public class FirstPassSpecifier : IPassSpecifier
method CheckNeeds (line 8) | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress prog...
FILE: ModuleManager/Patches/PassSpecifiers/ForPassSpecifier.cs
class ForPassSpecifier (line 6) | public class ForPassSpecifier : IPassSpecifier
method ForPassSpecifier (line 11) | public ForPassSpecifier(string mod, UrlDir.UrlConfig urlConfig)
method CheckNeeds (line 18) | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress prog...
FILE: ModuleManager/Patches/PassSpecifiers/IPassSpecifier.cs
type IPassSpecifier (line 6) | public interface IPassSpecifier
method CheckNeeds (line 8) | bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress progress);
FILE: ModuleManager/Patches/PassSpecifiers/InsertPassSpecifier.cs
class InsertPassSpecifier (line 6) | public class InsertPassSpecifier : IPassSpecifier
method CheckNeeds (line 8) | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress prog...
FILE: ModuleManager/Patches/PassSpecifiers/LastPassSpecifier.cs
class LastPassSpecifier (line 6) | public class LastPassSpecifier : IPassSpecifier
method LastPassSpecifier (line 10) | public LastPassSpecifier(string mod)
method CheckNeeds (line 16) | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress prog...
FILE: ModuleManager/Patches/PassSpecifiers/LegacyPassSpecifier.cs
class LegacyPassSpecifier (line 6) | public class LegacyPassSpecifier : IPassSpecifier
method CheckNeeds (line 8) | public bool CheckNeeds(INeedsChecker needsChecker, IPatchProgress prog...
FILE: ModuleManager/Patches/PatchCompiler.cs
type IPatchCompiler (line 5) | public interface IPatchCompiler
method CompilePatch (line 7) | IPatch CompilePatch(ProtoPatch protoPatch);
class PatchCompiler (line 10) | public class PatchCompiler : IPatchCompiler
method CompilePatch (line 12) | public IPatch CompilePatch(ProtoPatch protoPatch)
FILE: ModuleManager/Patches/ProtoPatch.cs
class ProtoPatch (line 6) | public class ProtoPatch
method ProtoPatch (line 16) | public ProtoPatch(UrlDir.UrlConfig urlConfig, Command command, string ...
FILE: ModuleManager/Patches/ProtoPatchBuilder.cs
type IProtoPatchBuilder (line 9) | public interface IProtoPatchBuilder
method Build (line 11) | ProtoPatch Build(UrlDir.UrlConfig urlConfig, Command command, ITagList...
class ProtoPatchBuilder (line 14) | public class ProtoPatchBuilder : IProtoPatchBuilder
method ProtoPatchBuilder (line 18) | public ProtoPatchBuilder(IPatchProgress progress)
method Build (line 23) | public ProtoPatch Build(UrlDir.UrlConfig urlConfig, Command command, I...
FILE: ModuleManager/PostPatchLoader.cs
class PostPatchLoader (line 18) | public class PostPatchLoader : LoadingSystem
method AddPostPatchCallback (line 32) | public static void AddPostPatchCallback(ModuleManagerPostPatchCallback...
method Awake (line 38) | [SuppressMessage("CodeQuality", "IDE0051", Justification = "Called by ...
method IsReady (line 49) | public override bool IsReady() => ready;
method LoadWeight (line 51) | public override float LoadWeight() => 0;
method ProgressFraction (line 53) | public override float ProgressFraction() => 1;
method ProgressTitle (line 55) | public override string ProgressTitle() => progressTitle;
method StartLoad (line 57) | public override void StartLoad()
method Run (line 63) | private IEnumerator Run()
method LoadModdedPhysics (line 215) | private void LoadModdedPhysics()
FILE: ModuleManager/Progress/IPatchProgress.cs
type IPatchProgress (line 5) | public interface IPatchProgress
method Warning (line 14) | void Warning(UrlDir.UrlConfig url, string message);
method Error (line 15) | void Error(UrlDir.UrlConfig url, string message);
method Error (line 16) | void Error(string message);
method Exception (line 17) | void Exception(string message, Exception exception);
method Exception (line 18) | void Exception(UrlDir.UrlConfig url, string message, Exception excepti...
method NeedsUnsatisfiedRoot (line 19) | void NeedsUnsatisfiedRoot(UrlDir.UrlConfig url);
method NeedsUnsatisfiedNode (line 20) | void NeedsUnsatisfiedNode(UrlDir.UrlConfig url, string path);
method NeedsUnsatisfiedValue (line 21) | void NeedsUnsatisfiedValue(UrlDir.UrlConfig url, string path);
method NeedsUnsatisfiedBefore (line 22) | void NeedsUnsatisfiedBefore(UrlDir.UrlConfig url);
method NeedsUnsatisfiedFor (line 23) | void NeedsUnsatisfiedFor(UrlDir.UrlConfig url);
method NeedsUnsatisfiedAfter (line 24) | void NeedsUnsatisfiedAfter(UrlDir.UrlConfig url);
method ApplyingCopy (line 25) | void ApplyingCopy(IUrlConfigIdentifier original, UrlDir.UrlConfig patch);
method ApplyingDelete (line 26) | void ApplyingDelete(IUrlConfigIdentifier original, UrlDir.UrlConfig pa...
method ApplyingUpdate (line 27) | void ApplyingUpdate(IUrlConfigIdentifier original, UrlDir.UrlConfig pa...
method PatchAdded (line 28) | void PatchAdded();
method PatchApplied (line 29) | void PatchApplied();
method PassStarted (line 30) | void PassStarted(IPass pass);
FILE: ModuleManager/Progress/PatchProgress.cs
class PatchProgress (line 7) | public class PatchProgress : IPatchProgress
method PatchProgress (line 26) | public PatchProgress(IBasicLogger logger)
method PatchProgress (line 32) | public PatchProgress(IPatchProgress progress, IBasicLogger logger)
method PatchAdded (line 38) | public void PatchAdded()
method PassStarted (line 43) | public void PassStarted(IPass pass)
method ApplyingUpdate (line 50) | public void ApplyingUpdate(IUrlConfigIdentifier original, UrlDir.UrlCo...
method ApplyingCopy (line 56) | public void ApplyingCopy(IUrlConfigIdentifier original, UrlDir.UrlConf...
method ApplyingDelete (line 62) | public void ApplyingDelete(IUrlConfigIdentifier original, UrlDir.UrlCo...
method PatchApplied (line 68) | public void PatchApplied()
method NeedsUnsatisfiedRoot (line 74) | public void NeedsUnsatisfiedRoot(UrlDir.UrlConfig url)
method NeedsUnsatisfiedNode (line 80) | public void NeedsUnsatisfiedNode(UrlDir.UrlConfig url, string path)
method NeedsUnsatisfiedValue (line 85) | public void NeedsUnsatisfiedValue(UrlDir.UrlConfig url, string path)
method NeedsUnsatisfiedBefore (line 90) | public void NeedsUnsatisfiedBefore(UrlDir.UrlConfig url)
method NeedsUnsatisfiedFor (line 96) | public void NeedsUnsatisfiedFor(UrlDir.UrlConfig url)
method NeedsUnsatisfiedAfter (line 102) | public void NeedsUnsatisfiedAfter(UrlDir.UrlConfig url)
method Warning (line 108) | public void Warning(UrlDir.UrlConfig url, string message)
method Error (line 115) | public void Error(UrlDir.UrlConfig url, string message)
method Error (line 122) | public void Error(string message)
method Exception (line 128) | public void Exception(string message, Exception exception)
method Exception (line 134) | public void Exception(UrlDir.UrlConfig url, string message, Exception ...
method RecordWarningFile (line 140) | private void RecordWarningFile(UrlDir.UrlConfig url)
method RecordErrorFile (line 152) | private void RecordErrorFile(UrlDir.UrlConfig url)
FILE: ModuleManager/Progress/ProgressCounter.cs
class ProgressCounter (line 7) | public class ProgressCounter
FILE: ModuleManager/Properties/Resources.Designer.cs
class Resources (line 22) | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resource...
method Resources (line 31) | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Mic...
FILE: ModuleManager/ProtoUrlConfig.cs
type IUrlConfigIdentifier (line 5) | public interface IUrlConfigIdentifier
type IProtoUrlConfig (line 12) | public interface IProtoUrlConfig : IUrlConfigIdentifier
class ProtoUrlConfig (line 18) | public class ProtoUrlConfig : IProtoUrlConfig
method ProtoUrlConfig (line 26) | public ProtoUrlConfig(UrlDir.UrlFile urlFile, ConfigNode node)
FILE: ModuleManager/Tags/Tag.cs
type Tag (line 5) | public struct Tag
method Tag (line 11) | public Tag(string key, string value, string trailer)
method ToString (line 25) | public override string ToString()
FILE: ModuleManager/Tags/TagList.cs
type ITagList (line 9) | public interface ITagList : IEnumerable<Tag>
class TagList (line 14) | public class TagList : ITagList
method TagList (line 18) | public TagList(Tag primaryTag, IEnumerable<Tag> tags)
method GetEnumerator (line 26) | public ArrayEnumerator<Tag> GetEnumerator() => new ArrayEnumerator<Tag...
method GetEnumerator (line 27) | IEnumerator<Tag> IEnumerable<Tag>.GetEnumerator() => GetEnumerator();
method GetEnumerator (line 28) | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
FILE: ModuleManager/Tags/TagListParser.cs
type ITagListParser (line 7) | public interface ITagListParser
method Parse (line 9) | ITagList Parse(string ToParse, UrlDir.UrlConfig urlConfig);
class TagListParser (line 12) | public class TagListParser : ITagListParser
method TagListParser (line 16) | public TagListParser(IPatchProgress progress)
method Parse (line 21) | public ITagList Parse(string toParse, UrlDir.UrlConfig urlConfig)
method ParsePrimaryTag (line 40) | private Tag ParsePrimaryTag(string toParse, ref List<Tag> tags, UrlDir...
method ParsePrimaryTrailer (line 65) | private Tag ParsePrimaryTrailer(string toParse, int start, ref List<Ta...
method ParseTag (line 91) | private void ParseTag(string toParse, int start, ref List<Tag> tags, U...
method ParseTrailer (line 125) | private void ParseTrailer(string toParse, int start, ref List<Tag> tag...
method ClosingBracketIndex (line 152) | private static int ClosingBracketIndex(string toParse, int start)
FILE: ModuleManager/Threading/BackgroundTask.cs
class BackgroundTask (line 6) | public static class BackgroundTask
method Start (line 8) | public static ITaskStatus Start(Action action)
FILE: ModuleManager/Threading/ITaskStatus.cs
type ITaskStatus (line 5) | public interface ITaskStatus
FILE: ModuleManager/Threading/TaskStatus.cs
class TaskStatus (line 5) | public class TaskStatus : ITaskStatus
method Finished (line 34) | public void Finished()
method Error (line 43) | public void Error(Exception exception)
FILE: ModuleManager/Threading/TaskStatusWrapper.cs
class TaskStatusWrapper (line 5) | public class TaskStatusWrapper : ITaskStatus
method TaskStatusWrapper (line 9) | public TaskStatusWrapper(ITaskStatus inner)
FILE: ModuleManager/Utils/Counter.cs
class Counter (line 5) | public class Counter
method Increment (line 9) | public void Increment()
method ToString (line 14) | public override string ToString()
FILE: ModuleManager/Utils/FileUtils.cs
class FileUtils (line 8) | public static class FileUtils
method FileSHA (line 10) | public static string FileSHA(string filename)
FILE: ModuleManagerTests/Collections/ArrayEnumeratorTest.cs
class ArrayEnumeratorTest (line 8) | public class ArrayEnumeratorTest
method Test__Constructor__ArrayNull (line 10) | [Fact]
method Test__Constructor__StartIndex__Negative (line 21) | [Fact]
method Test__Constructor__StartIndex__TooLarge (line 34) | [Fact]
method Test__Constructor__Length__Negative (line 47) | [Fact]
method Test__Constructor__Length__TooLong (line 60) | [Fact]
method TestArrayEnumerator (line 73) | [Fact]
method TestArrayEnumerator__Reset (line 89) | [Fact]
method TestArrayEnumerator__Empty (line 111) | [Fact]
method TestArrayEnumerator__StartIndex (line 119) | [Fact]
method TestArrayEnumerator__StartIndex__Length (line 133) | [Fact]
FILE: ModuleManagerTests/Collections/ImmutableStackTest.cs
class ImmutableStackTest (line 9) | public class ImmutableStackTest
method TestValue (line 11) | [Fact]
method TestIsRoot (line 20) | [Fact]
method TestRoot (line 30) | [Fact]
method TestDepth (line 42) | [Fact]
method TestPush (line 54) | [Fact]
method TestPop (line 72) | [Fact]
method TestPop__Root (line 85) | [Fact]
method TestReplaceValue (line 96) | [Fact]
method TestReplaceValue__Root (line 111) | [Fact]
method TestEnumerator (line 124) | [Fact]
FILE: ModuleManagerTests/Collections/KeyValueCacheTest.cs
class KeyValueCacheTest (line 7) | public class KeyValueCacheTest
method TestFetch__CreateValueNull (line 9) | [Fact]
method TestFetch__KeyNotPresent (line 21) | [Fact]
method TestFetch__KeyPresent (line 33) | [Fact]
FILE: ModuleManagerTests/Collections/MessageQueueTest.cs
class MessageQueueTest (line 7) | public class MessageQueueTest
class TestClass (line 9) | private class TestClass { }
method Test__Empty (line 13) | [Fact]
method TestAdd (line 19) | [Fact]
method TestTakeAll (line 33) | [Fact]
FILE: ModuleManagerTests/CommandParserTest.cs
class CommandParserTest (line 10) | public class CommandParserTest
method TestParse__Insert (line 12) | [Fact]
method TestParse__Delete (line 19) | [Fact]
method TestParse__Edit (line 28) | [Fact]
method TestParse__Replace (line 35) | [Fact]
method TestParse__Copy (line 42) | [Fact]
method TestParse__Rename (line 51) | [Fact]
method TestParse__Paste (line 58) | [Fact]
method TestParse__Special (line 65) | [Fact]
method TestParse__Special__Chained (line 72) | [Fact]
method TestParse__Create (line 79) | [Fact]
FILE: ModuleManagerTests/DummyTest.cs
class DummyTest (line 6) | public class DummyTest
method PassingTest (line 8) | [Fact]
FILE: ModuleManagerTests/Extensions/ByteArrayExtensionsTest.cs
class ByteArrayExtensionsTest (line 7) | public class ByteArrayExtensionsTest
method TestToHex (line 9) | [Fact]
method TestToHex__NullData (line 17) | [Fact]
FILE: ModuleManagerTests/Extensions/ConfigNodeExtensionsTest.cs
class ConfigNodeExtensionsTest (line 9) | public class ConfigNodeExtensionsTest
method TestShallowCopyFrom (line 11) | [Fact]
method TestDeepCopy (line 82) | [Fact]
method TestPrettyPrint (line 143) | [Fact]
method TestPrettyPrint__NullNode (line 190) | [Fact]
method TestPrettyPrint__NullStringBuilder (line 199) | [Fact]
method TestPrettyPrint__NullIndent (line 210) | [Fact]
method TestPrettyPrint__NullName (line 240) | [Fact]
method TestAddValueSafe (line 272) | [Fact]
method TestEscapeValuesRecursive (line 287) | [Fact]
method TestUnescapeValuesRecursive (line 310) | [Fact]
method AssertValue (line 333) | private void AssertValue(string name, string value, ConfigNode.Value n...
FILE: ModuleManagerTests/Extensions/IBasicLoggerExtensionsTest.cs
class IBasicLoggerExtensionsTest (line 9) | public class IBasicLoggerExtensionsTest
method TestInfo (line 13) | [Fact]
method TestWarning (line 20) | [Fact]
method TestError (line 27) | [Fact]
method TestException (line 34) | [Fact]
method TestException__Null (line 42) | [Fact]
method TestException__Message (line 53) | [Fact]
method TestException__Message__MessageNull (line 61) | [Fact]
method TestException__Message__ExceptionNull (line 72) | [Fact]
FILE: ModuleManagerTests/Extensions/NodeStackExtensionsTest.cs
class NodeStackExtensionsTest (line 8) | public class NodeStackExtensionsTest
method TestGetPath (line 10) | [Fact]
FILE: ModuleManagerTests/Extensions/StringExtensionsTest.cs
class StringExtensionsTest (line 10) | public class StringExtensionsTest
method TestIsBracketBalanced (line 12) | [Fact]
method TestIsBracketBalanced__NoBrackets (line 18) | [Fact]
method TestIsBracketBalanced__Unbalanced (line 24) | [Fact]
method TestIsBracketBalanced__BalancedButNegative (line 31) | [Fact]
method TestRemoveWS (line 37) | [Fact]
method TestContains (line 44) | [InlineData("abc", "b", true, 1)]
method TestContains__NullStr (line 54) | [Fact]
method TestContains__NullValue (line 64) | [Fact]
FILE: ModuleManagerTests/Extensions/UrlConfigExtensionsTest.cs
class UrlConfigExtensionsTest (line 8) | public class UrlConfigExtensionsTest
method TestSafeUrl (line 10) | [Fact]
method TestSafeUrl__Null (line 21) | [Fact]
method TestSafeUrl__NullParent (line 28) | [Fact]
method TestSafeUrl__NullParent__NullName (line 35) | [Fact]
method TestSafeUrl__NullParent__BlankName (line 46) | [Fact]
method TestSafeUrl__NullName (line 53) | [Fact]
method TestSafeUrl__BlankName (line 65) | [Fact]
method TestPrettyPrint (line 76) | [Fact]
method TestPrettyPrint__NullName (line 107) | [Fact]
method TestPrettyPrint__BlankName (line 140) | [Fact]
method TestPrettyPrint__NullNameValue (line 171) | [Fact]
method TestPrettyPrint__NullNode (line 206) | [Fact]
method TestPrettyPrint__Null (line 220) | [Fact]
FILE: ModuleManagerTests/Extensions/UrlDirExtensionsTest.cs
class UrlDirExtensionsTest (line 8) | public class UrlDirExtensionsTest
method TestFind__IndirectChild (line 11) | [Fact]
method TestFind__DirectChild (line 20) | [Fact]
method TestFind__Extension (line 29) | [Fact]
method TestFind__NotFound (line 40) | [Fact]
method TestFind__Extension__NotFound (line 49) | [Fact]
method TestFind__IntermediateDirectoryNotFound (line 59) | [Fact]
method TestFind__UrlDirNull (line 66) | [Fact]
method TestFind__UrlNull (line 77) | [Fact]
FILE: ModuleManagerTests/Extensions/UrlFileExtensionsTest.cs
class UrlFileExtensionsTest (line 8) | public static class UrlFileExtensionsTest
method TestGetUrlWithExtension (line 10) | [Fact]
FILE: ModuleManagerTests/InGameTestRunnerTest.cs
class InGameTestRunnerTest (line 12) | public class InGameTestRunnerTest
method InGameTestRunnerTest (line 18) | public InGameTestRunnerTest()
method TestConstructor__LoggerNull (line 25) | [Fact]
method TestRunTestCases__DatabaseRootNull (line 36) | [Fact]
method TestRunTestCases__WrongNumberOfNodes (line 47) | [Fact]
method TestRunTestCases__AllPassing (line 90) | [Fact]
method TestRunTestCases__Failure (line 144) | [Fact]
FILE: ModuleManagerTests/Logging/LogMessageTest.cs
class LogMessageTest (line 9) | public class LogMessageTest
method TestConstructor (line 11) | [Fact]
method TestConstructor__NullMessage (line 21) | [Fact]
method TestConstructor__FromOtherMessage (line 32) | [Fact]
method TestConstructor__FromOtherMessage__LogMessageNull (line 45) | [Fact]
method TestConstructor__FromOtherMessage__NewMessageNull (line 56) | [Fact]
method TestToLogMessage__Info (line 67) | [Fact]
method TestToLogMessage__Warning (line 74) | [Fact]
method TestToLogMessage__Error (line 81) | [Fact]
method TestToLogMessage__Exception (line 88) | [Fact]
method TestToLogMessage__Assert (line 95) | [Fact]
method TestToLogMessage__Unknown (line 102) | [Fact]
method TestToString (line 109) | [Fact]
method TestToLogMessage__Timestamp (line 116) | [Fact]
FILE: ModuleManagerTests/Logging/LogSplitterTest.cs
class LogSplitterTest (line 9) | public class LogSplitterTest
method TestConstructor__Logger1Null (line 11) | [Fact]
method TestConstructor__Logger2Null (line 22) | [Fact]
method TestLog (line 33) | [Fact]
method TestLog__MessageNull (line 45) | [Fact]
FILE: ModuleManagerTests/Logging/PrefixLoggerTest.cs
class PrefixLoggerTest (line 9) | public class PrefixLoggerTest
method PrefixLoggerTest (line 14) | public PrefixLoggerTest()
method TestConstructor__PrefixNull (line 19) | [Fact]
method TestConstructor__PrefixBlank (line 30) | [Fact]
method TestConstructor__LoggerNull (line 41) | [Fact]
method TestLog (line 52) | [Fact]
method TestLog__Null (line 69) | [Fact]
FILE: ModuleManagerTests/Logging/QueueLogRunnerTest.cs
class QueueLogRunnerTest (line 10) | public class QueueLogRunnerTest
method TestConstructor__LogQueueNull (line 12) | [Fact]
method TestConstructor__TimeToWaitForLogsMsNegative (line 23) | [Fact]
method TestRun (line 35) | [Fact]
method TestRun__AlreadyStarted (line 85) | [Fact]
method TestRun__LoggerNull (line 128) | [Fact]
method TestRequestStop__NotStarted (line 140) | [Fact]
FILE: ModuleManagerTests/Logging/QueueLoggerTest.cs
class QueueLoggerTest (line 9) | public class QueueLoggerTest
method QueueLoggerTest (line 14) | public QueueLoggerTest()
method TestConstructor__QueueNull (line 19) | [Fact]
method TestLog (line 30) | [Fact]
method TestLog__MessageNull (line 38) | [Fact]
FILE: ModuleManagerTests/Logging/StreamLoggerTest.cs
class StreamLoggerTest (line 9) | public class StreamLoggerTest
method TestConstructor__StreamNull (line 11) | [Fact]
method TestConstructor__CantWrite (line 20) | [Fact]
method TestLog__AlreadyDisposed (line 32) | [Fact]
method TestLog (line 49) | [Fact]
method TestLog__MessageNull (line 73) | [Fact]
FILE: ModuleManagerTests/Logging/UnityLoggerTest.cs
class UnityLoggerTest (line 10) | public class UnityLoggerTest
method UnityLoggerTest (line 15) | public UnityLoggerTest()
method TestConstructor__LoggerNull (line 20) | [Fact]
method TestLog__Info (line 31) | [Fact]
method TestLog__Warning (line 39) | [Fact]
method TestLog__MessageNull (line 47) | [Fact]
FILE: ModuleManagerTests/LoggingAssertionHelpers.cs
class LoggingAssertionHelpers (line 8) | public static class LoggingAssertionHelpers
method AssertInfo (line 10) | public static void AssertInfo(this IBasicLogger logger, string message)
method AssertNoInfo (line 16) | public static void AssertNoInfo(this IBasicLogger logger)
method AssertWarning (line 22) | public static void AssertWarning(this IBasicLogger logger, string mess...
method AssertNoWarning (line 28) | public static void AssertNoWarning(this IBasicLogger logger)
method AssertError (line 34) | public static void AssertError(this IBasicLogger logger, string message)
method AssertNoError (line 40) | public static void AssertNoError(this IBasicLogger logger)
method AssertException (line 46) | public static void AssertException(this IBasicLogger logger, string me...
method AssertException (line 52) | public static void AssertException(this IBasicLogger logger, Exception...
method AssertNoException (line 58) | public static void AssertNoException(this IBasicLogger logger)
method AssertNoLog (line 64) | public static void AssertNoLog(this IBasicLogger logger)
FILE: ModuleManagerTests/MMPatchLoaderTest.cs
class MMPatchLoaderTest (line 15) | public class MMPatchLoaderTest
method TestModifyNode__IndexAllWithAssign (line 20) | [Fact]
method TestModifyNode__MultiplyValue (line 47) | [Fact]
method TestModifyNode__EditNode__SpecialCharacters (line 74) | [Fact]
method TestModifyNode__ReplaceNode__SpecialCharacters (line 109) | [Fact]
method AssertConfigNodesEqual (line 152) | private void AssertConfigNodesEqual(ConfigNode expected, ConfigNode ob...
method EnsureNoErrors (line 157) | private void EnsureNoErrors()
FILE: ModuleManagerTests/NeedsCheckerTest.cs
class NeedsCheckerTest (line 11) | public class NeedsCheckerTest
method NeedsCheckerTest (line 19) | public NeedsCheckerTest()
method TestConstructor__ModsNull (line 25) | [Fact]
method TestConstructor__GameDataNull (line 36) | [Fact]
method TestConstructor__ProgressNull (line 47) | [Fact]
method TestConstructor__LoggerNull (line 58) | [Fact]
method TestCheckNeedsExpression (line 69) | [Fact]
method TestCheckNeedsExpression__AndOr (line 77) | [Fact]
method TestCheckNeedsExpression__Not (line 100) | [Fact]
method TestCheckNeedsExpression__Capitalization (line 113) | [Fact]
method TestCheckNeedsExpression__Directory (line 125) | [Fact]
method TestCheckNeedsExpression__Null (line 148) | [Fact]
method TestCheckNeedsExpression__Empty (line 159) | [Fact]
method TestCheckNeeds (line 171) | [Fact]
method TestCheckNeeds__Null (line 184) | [Fact]
method TestCheckNeeds__Empty (line 195) | [Fact]
method TestCheckNeedsRecursive (line 207) | [Fact]
method TestCheckNeedsRecursive__NodeNull (line 360) | [Fact]
method TestCheckNeedsRecursive__UrlConfigNull (line 372) | [Fact]
FILE: ModuleManagerTests/NodeMatcherTest.cs
class NodeMatcherTest (line 8) | public class NodeMatcherTest
method TestConstructor__TypeNull (line 12) | [Fact]
method TestConstructor__TypeBlank (line 23) | [Fact]
method TestConstructor__NameBlank (line 35) | [Fact]
method TestConstructor__ConstraintsBlank (line 47) | [Fact]
method TestConstructor__ConstraintsNotBracketBalanced (line 59) | [Fact]
method TestIsMatch (line 75) | [Fact]
method TestIsMatch__Name (line 84) | [Fact]
method TestIsMatch__Name__Wildcard (line 114) | [Fact]
method TestIsMatch__Name__Multiple (line 154) | [Fact]
method TestIsMatch__Constraints (line 209) | [Fact]
method TestIsMatch__Constraints_Open (line 265) | [Fact]
FILE: ModuleManagerTests/OperatorParserTest.cs
class OperatorParserTest (line 7) | public class OperatorParserTest
method TestParse__Null (line 9) | [Fact]
method TestParse__Empty (line 20) | [Fact]
method TestParse__Assign (line 29) | [Fact]
method TestParse__Add (line 38) | [Fact]
method TestParse__Subtract (line 47) | [Fact]
method TestParse__Multiply (line 56) | [Fact]
method TestParse__Divide (line 65) | [Fact]
method TestParse__Exponentiate (line 74) | [Fact]
method TestParse__RegexReplace (line 83) | [Fact]
method TestParse__NoSpaceMeansNoOp (line 92) | [Fact]
method TestParse__SingleCharacterNotOp (line 101) | [Fact]
FILE: ModuleManagerTests/PassTest.cs
class PassTest (line 10) | public class PassTest
method TestConstructor__NameNull (line 12) | [Fact]
method TestConstructor__NameEmpty (line 23) | [Fact]
method TestName (line 35) | [Fact]
method Test__Add__Enumerator (line 43) | [Fact]
FILE: ModuleManagerTests/PatchApplierTest.cs
class PatchApplierTest (line 14) | public class PatchApplierTest
method TestConstructor__ProgressNull (line 16) | [Fact]
method TestConstructor__LoggerNull (line 27) | [Fact]
method TestApplyPatches__PatchesNull (line 38) | [Fact]
method TestApplyPatches (line 50) | [Fact]
FILE: ModuleManagerTests/PatchExtractorTest.cs
class PatchExtractorTest (line 14) | public class PatchExtractorTest
method PatchExtractorTest (line 27) | public PatchExtractorTest()
method TestConstructor__ProgressNull (line 41) | [Fact]
method TestConstructor__LoggerNull (line 52) | [Fact]
method TestConstructor__NeedsCheckerNull (line 63) | [Fact]
method TestConstructor__TagListParserNull (line 74) | [Fact]
method TestConstructor__ProtoPatchBuilderNull (line 85) | [Fact]
method TestConstructor__PatchCompilerNull (line 96) | [Fact]
method TestExtractPatch__ProtoPatchNull (line 107) | [Fact]
method TestExtractPatch (line 124) | [Fact]
method TestExtractPatch__Needs (line 158) | [Fact]
method TestExtractPatch__NeedsUnsatisfied (line 192) | [Fact]
method TestExtractPatch__NeedsUnsatisfiedPassSpecifier (line 225) | [Fact]
method TestExtractPatch__Null (line 258) | [Fact]
method TestExtractPatch__NotBracketBalanced (line 269) | [Fact]
method TestExtractPatch__InvalidCommand__Replace (line 290) | [Fact]
method TestExtractPatch__InvalidCommand__Create (line 299) | [Fact]
method TestExtractPatch__InvalidCommand__Rename (line 308) | [Fact]
method TestExtractPatch__InvalidCommand__Paste (line 317) | [Fact]
method TestExtractPatch__InvalidCommand__Special (line 326) | [Fact]
method TestExtractPatch__TagListBadlyFormatted (line 335) | [Fact]
method TestExtractPatch__ProtoPatchFailed (line 345) | [Fact]
method TestExtractPatch__Exception (line 355) | [Fact]
method CreateConfig (line 366) | private UrlDir.UrlConfig CreateConfig(string name)
method AssertNoErrors (line 382) | private void AssertNoErrors()
FILE: ModuleManagerTests/PatchListTest.cs
class PatchListTest (line 14) | public class PatchListTest
method TestConstructor__ModListNull (line 16) | [Fact]
method TestConstructor__PatchesNull (line 27) | [Fact]
method TestConstructor__ProgressNull (line 38) | [Fact]
method TestConstructor__UnknownMod (line 49) | [Fact]
method TestConstructor__UnknownPassSpecifier (line 67) | [Fact]
method Test__Lifecycle (line 87) | [Fact]
FILE: ModuleManagerTests/Patches/CopyPatchTest.cs
class CopyPatchTest (line 15) | public class CopyPatchTest
method TestConstructor__urlConfigNull (line 17) | [Fact]
method TestConstructor__nodeMatcherNull (line 28) | [Fact]
method TestConstructor__passSpecifierNull (line 39) | [Fact]
method TestUrlConfig (line 50) | [Fact]
method TestNodeMatcher (line 59) | [Fact]
method TestPassSpecifier (line 68) | [Fact]
method TestCountsAsPatch (line 77) | [Fact]
method TestApply (line 84) | [Fact]
method TestApply__NameChanged (line 190) | [Fact]
method TestApply__NameNotChanged (line 253) | [Fact]
method TestApply__DatabaseConfigsNullNull (line 303) | [Fact]
method TestApply__ProgressNull (line 315) | [Fact]
method TestApply__LoggerNull (line 327) | [Fact]
method AssertNodesEqual (line 339) | private void AssertNodesEqual(ConfigNode expected, ConfigNode actual)
FILE: ModuleManagerTests/Patches/DeletePatchTest.cs
class DeletePatchTest (line 14) | public class DeletePatchTest
method TestConstructor__urlConfigNull (line 16) | [Fact]
method TestConstructor__nodeMatcherNull (line 27) | [Fact]
method TestConstructor__passSpecifierNull (line 38) | [Fact]
method TestUrlConfig (line 49) | [Fact]
method TestNodeMatcher (line 58) | [Fact]
method TestPassSpecifier (line 67) | [Fact]
method TestCountsAsPatch (line 76) | [Fact]
method TestApply (line 83) | [Fact]
method TestApply__DatabaseConfigsNull (line 137) | [Fact]
method TestApply__ProgressNull (line 149) | [Fact]
method TestApply__LoggerNull (line 161) | [Fact]
FILE: ModuleManagerTests/Patches/EditPatchTest.cs
class EditPatchTest (line 16) | public class EditPatchTest
method TestConstructor__urlConfigNull (line 18) | [Fact]
method TestConstructor__nodeMatcherNull (line 29) | [Fact]
method TestConstructor__passSpecifierNull (line 40) | [Fact]
method TestUrlConfig (line 51) | [Fact]
method TestNodeMatcher (line 60) | [Fact]
method TestPassSpecifier (line 69) | [Fact]
method TestCountsAsPatch (line 78) | [Fact]
method TestApply (line 85) | [Fact]
method TestApply__Loop (line 182) | [Fact]
method TestApply__DatabaseConfigsNull (line 254) | [Fact]
method TestApply__ProgressNull (line 266) | [Fact]
method TestApply__LoggerNull (line 278) | [Fact]
method AssertNodesEqual (line 290) | private void AssertNodesEqual(ConfigNode expected, ConfigNode actual)
FILE: ModuleManagerTests/Patches/InsertPatchTest.cs
class InsertPatchTest (line 16) | public class InsertPatchTest
method TestConstructor__UrlConfigNull (line 18) | [Fact]
method TestConstructor__NodeTypeNull (line 29) | [Fact]
method TestConstructor__PassSpecifierNull (line 40) | [Fact]
method TestUrlConfig (line 51) | [Fact]
method TestNodeType (line 60) | [Fact]
method TestPassSpecifier (line 68) | [Fact]
method TestCountsAsPatch (line 77) | [Fact]
method TestApply (line 84) | [Fact]
method TestApply__DatabaseConfigsNull (line 144) | [Fact]
method TestApply__ProgressNull (line 156) | [Fact]
method TestApply__LoggerNull (line 168) | [Fact]
FILE: ModuleManagerTests/Patches/PassSpecifiers/AfterPassSpecifierTest.cs
class AfterPassSpecifierTest (line 11) | public class AfterPassSpecifierTest
method AfterPassSpecifierTest (line 18) | public AfterPassSpecifierTest()
method TestConstructor__ModNull (line 23) | [Fact]
method TestConstructor__ModEmpty (line 34) | [Fact]
method TestConstructor__UrlConfigNull (line 46) | [Fact]
method TestCheckNeeds__False (line 57) | [Fact]
method TestCheckNeeds__True (line 66) | [Fact]
method TestCheckNeeds__NeedsCheckerNull (line 75) | [Fact]
method TestCheckNeeds__ProgressNull (line 88) | [Fact]
method TestDescriptor (line 99) | [Fact]
FILE: ModuleManagerTests/Patches/PassSpecifiers/BeforePassSpecifierTest.cs
class BeforePassSpecifierTest (line 11) | public class BeforePassSpecifierTest
method BeforePassSpecifierTest (line 18) | public BeforePassSpecifierTest()
method TestConstructor__ModNull (line 23) | [Fact]
method TestConstructor__ModEmpty (line 34) | [Fact]
method TestConstructor__UrlConfigNull (line 46) | [Fact]
method TestCheckNeeds__False (line 57) | [Fact]
method TestCheckNeeds__True (line 66) | [Fact]
method TestCheckNeeds__NeedsCheckerNull (line 75) | [Fact]
method TestCheckNeeds__ProgressNull (line 88) | [Fact]
method TestDescriptor (line 99) | [Fact]
FILE: ModuleManagerTests/Patches/PassSpecifiers/FinalPassSpecifierTest.cs
class FinalPassSpecifierrTest (line 10) | public class FinalPassSpecifierrTest
method TestCheckNeeds (line 16) | [Fact]
method TestCheckNeeds__NeedsCheckerNull (line 22) | [Fact]
method TestCheckNeeds__ProgressNull (line 35) | [Fact]
method TestDescriptor (line 46) | [Fact]
FILE: ModuleManagerTests/Patches/PassSpecifiers/FirstPassSpecifierTest.cs
class FirstPassSpecifierTest (line 10) | public class FirstPassSpecifierTest
method TestCheckNeeds (line 16) | [Fact]
method TestCheckNeeds__NeedsCheckerNull (line 22) | [Fact]
method TestCheckNeeds__ProgressNull (line 35) | [Fact]
method TestDescriptor (line 46) | [Fact]
FILE: ModuleManagerTests/Patches/PassSpecifiers/ForPassSpecifierTest.cs
class ForPassSpecifierTest (line 11) | public class ForPassSpecifierTest
method ForPassSpecifierTest (line 18) | public ForPassSpecifierTest()
method TestConstructor__ModNull (line 23) | [Fact]
method TestConstructor__ModEmpty (line 34) | [Fact]
method TestConstructor__UrlConfigNull (line 46) | [Fact]
method TestCheckNeeds__False (line 57) | [Fact]
method TestCheckNeeds__True (line 66) | [Fact]
method TestCheckNeeds__NeedsCheckerNull (line 75) | [Fact]
method TestCheckNeeds__ProgressNull (line 88) | [Fact]
method TestDescriptor (line 99) | [Fact]
FILE: ModuleManagerTests/Patches/PassSpecifiers/InsertPassSpecifierTest.cs
class InsertPassSpecifierTest (line 10) | public class InsertPassSpecifierTest
method TestCheckNeeds (line 16) | [Fact]
method TestCheckNeeds__NeedsCheckerNull (line 22) | [Fact]
method TestCheckNeeds__ProgressNull (line 35) | [Fact]
method TestDescriptor (line 46) | [Fact]
FILE: ModuleManagerTests/Patches/PassSpecifiers/LastPassSpecifierTest.cs
class LastPassSpecifierTest (line 10) | public class LastPassSpecifierTest
method LastPassSpecifierTest (line 16) | public LastPassSpecifierTest()
method TestConstructor__ModNull (line 21) | [Fact]
method TestConstructor__ModEmpty (line 32) | [Fact]
method TestCheckNeeds (line 44) | [Fact]
method TestDescriptor (line 52) | [Fact]
FILE: ModuleManagerTests/Patches/PassSpecifiers/LegacyPassSpecifierTest.cs
class LegacyPassSpecifierTest (line 10) | public class LegacyPassSpecifierTest
method TestCheckNeeds (line 16) | [Fact]
method TestCheckNeeds__NeedsCheckerNull (line 22) | [Fact]
method TestCheckNeeds__ProgressNull (line 35) | [Fact]
method TestDescriptor (line 46) | [Fact]
FILE: ModuleManagerTests/Patches/PatchCompilerTest.cs
class PatchCompilerTest (line 15) | public class PatchCompilerTest
method TestCompilePatch__Insert (line 22) | [Fact]
method TestCompilePatch__Edit (line 57) | [Fact]
method TestCompilePatch__Copy (line 107) | [Fact]
method TestCompilePatch__Delete (line 165) | [Fact]
method TestCompilePatch__NullProtoPatch (line 202) | [Fact]
method TestCompilePatch__InvalidCommand__Replace (line 213) | [Fact]
method TestCompilePatch__InvalidCommand__Create (line 235) | [Fact]
method TestCompilePatch__InvalidCommand__Rename (line 257) | [Fact]
method TestCompilePatch__InvalidCommand__Paste (line 279) | [Fact]
method TestCompilePatch__InvalidCommand__Special (line 301) | [Fact]
method AssertNodeMatcher (line 323) | private void AssertNodeMatcher(INodeMatcher matcher)
method AssertNodesEqual (line 357) | private void AssertNodesEqual(ConfigNode expected, ConfigNode actual)
method AssertNoErrors (line 362) | private void AssertNoErrors()
FILE: ModuleManagerTests/Patches/ProtoPatchBuilderTest.cs
class ProtoPatchBuilderTest (line 14) | public class ProtoPatchBuilderTest
method ProtoPatchBuilderTest (line 20) | public ProtoPatchBuilderTest()
method TestBuild__PrimaryValueNull (line 26) | [Fact]
method TestBuild__PrimaryValue (line 45) | [Fact]
method TestBuild__Needs (line 64) | [Fact]
method TestBuild__Needs__Case1 (line 86) | [Fact]
method TestBuild__Needs__Case2 (line 108) | [Fact]
method TestBuild__Has (line 130) | [Fact]
method TestBuild__Has__Case1 (line 152) | [Fact]
method TestBuild__Has__Case2 (line 174) | [Fact]
method TestBuild__First (line 196) | [Fact]
method TestBuild__First__Case1 (line 218) | [Fact]
method TestBuild__First__Case2 (line 240) | [Fact]
method TestBuild__Before (line 262) | [Fact]
method TestBuild__Before__Case1 (line 286) | [Fact]
method TestBuild__Before__Case2 (line 310) | [Fact]
method TestBuild__For (line 334) | [Fact]
method TestBuild__For__Case1 (line 358) | [Fact]
method TestBuild__For__Case2 (line 382) | [Fact]
method TestBuild__After (line 406) | [Fact]
method TestBuild__After__Case1 (line 430) | [Fact]
method TestBuild__After__Case2 (line 454) | [Fact]
method TestBuild__Last (line 478) | [Fact]
method TestBuild__Last__Case1 (line 501) | [Fact]
method TestBuild__Last__Case2 (line 524) | [Fact]
method TestBuild__Final (line 547) | [Fact]
method TestBuild__Final__Case1 (line 569) | [Fact]
method TestBuild__Final__Case2 (line 591) | [Fact]
method TestBuild__Insert__InsertPass (line 613) | [Fact]
method TestBuild__UrlConfigNull (line 635) | [Fact]
method TestBuild__TagListNull (line 646) | [Fact]
method TestBuild__PrimaryValueEmpty (line 657) | [Fact]
method TestBuild__NodeNameOnInsert (line 678) | [Fact]
method TestBuild__TrailerOnPrimaryTag (line 691) | [Fact]
method TestBuild__TrailerOnSomeTag (line 712) | [Fact]
method TestBuild__MoreThanOneNeeds (line 736) | [Fact]
method TestBuild__NullNeeds (line 761) | [Fact]
method TestBuild__EmptyNeeds (line 777) | [Fact]
method TestBuild__MoreThanOneHas (line 793) | [Fact]
method TestBuild__NullHas (line 818) | [Fact]
method TestBuild__EmptyHas (line 834) | [Fact]
method TestBuild__HasOnInsert (line 850) | [Fact]
method TestBuild__BracketsOnFirst (line 866) | [Fact]
method TestBuild__ValueOnFirst (line 890) | [Fact]
method TestBuild__MoreThanOnePass__First (line 914) | [Fact]
method TestBuild__PassSpecifierOnInsert__First (line 939) | [Fact]
method TestBuild__NullBefore (line 955) | [Fact]
method TestBuild__EmptyBefore (line 971) | [Fact]
method TestBuild__MoreThanOnePass__Before (line 987) | [Fact]
method TestBuild__PassSpecifierOnInsert__Before (line 1012) | [Fact]
method TestBuild__NullFor (line 1028) | [Fact]
method TestBuild__EmptyFor (line 1044) | [Fact]
method TestBuild__MoreThanOnePass__For (line 1060) | [Fact]
method TestBuild__PassSpecifierOnInsert__For (line 1085) | [Fact]
method TestBuild__NullAfter (line 1101) | [Fact]
method TestBuild__EmptyAfter (line 1117) | [Fact]
method TestBuild__MoreThanOnePass__After (line 1133) | [Fact]
method TestBuild__PassSpecifierOnInsert__After (line 1158) | [Fact]
method TestBuild__NullLast (line 1174) | [Fact]
method TestBuild__EmptyLast (line 1190) | [Fact]
method TestBuild__MoreThanOnePass__Last (line 1206) | [Fact]
method TestBuild__PassSpecifierOnInsert__Last (line 1231) | [Fact]
method TestBuild__BracketsOnFinal (line 1247) | [Fact]
method TestBuild__ValueOnFinal (line 1271) | [Fact]
method TestBuild__MoreThanOnePass__Final (line 1295) | [Fact]
method TestBuild__PassSpecifierOnInsert__Final (line 1320) | [Fact]
method TestBuild__UnrecognizedTag (line 1336) | [Fact]
method EnsureNoErrors (line 1360) | private void EnsureNoErrors()
method EnsureNoExceptions (line 1367) | private void EnsureNoExceptions()
FILE: ModuleManagerTests/Progress/PatchProgressTest.cs
class PatchProgressTest (line 11) | public class PatchProgressTest
method PatchProgressTest (line 16) | public PatchProgressTest()
method Test__Constructor__Nested (line 21) | [Fact]
method TestPatchAdded (line 41) | [Fact]
method TestApplyingUpdate (line 51) | [Fact]
method TesApplyingCopy (line 70) | [Fact]
method TesApplyingDelete (line 89) | [Fact]
method TestPatchApplied (line 108) | [Fact]
method TestNeedsUnsatisfiedRoot (line 122) | [Fact]
method TestNeedsUnsatisfiedNode (line 139) | [Fact]
method TestNeedsUnsatisfiedValue (line 156) | [Fact]
method TestNeedsUnsatisfiedBefore (line 173) | [Fact]
method TestNeedsUnsatisfiedFor (line 190) | [Fact]
method TestNeedsUnsatisfiedAfter (line 207) | [Fact]
method TestStartingPass (line 224) | [Fact]
method TestStartingPass__NullArgument (line 238) | [Fact]
method TestWarning (line 255) | [Fact]
method TestError (line 274) | [Fact]
method TestError__Config (line 286) | [Fact]
method TestException (line 306) | [Fact]
method TestException__Url (line 323) | [Fact]
method TestProgressFraction (line 345) | [Fact]
FILE: ModuleManagerTests/ProtoUrlConfigTest.cs
class ProtoUrlConfigTest (line 8) | public class ProtoUrlConfigTest
method TestContructor__UrlFileNull (line 10) | [Fact]
method TestContructor__NodeNull (line 21) | [Fact]
method TestUrlFile (line 32) | [Fact]
method TestNode (line 41) | [Fact]
method TestFileUrl (line 50) | [Fact]
method TestNodeType (line 58) | [Fact]
method TestFullUrl (line 66) | [Fact]
method TestFullUrl__NameValue (line 74) | [Fact]
FILE: ModuleManagerTests/Tags/TagListParserTest.cs
class TagListParserTest (line 10) | public class TagListParserTest
method TagListParserTest (line 16) | public TagListParserTest()
method TestConstructor__ProgressNull (line 21) | [Fact]
method TestParse__OnlyPrimaryKey (line 32) | [Fact]
method TestParse__OnlyPrimaryKeyAndValue (line 40) | [Fact]
method TestParse__OnlyPrimaryKeyValueAndTrailer (line 48) | [Fact]
method TestParse__OnlyPrimaryKeyValueAndTrailer__ValueHasSomeStuff (line 56) | [Fact]
method TestParse__TagWithOnlyKey (line 64) | [Fact]
method TestParse__TagWithOnlyKeyAndValue (line 75) | [Fact]
method TestParse__FullTags (line 86) | [Fact]
method TestParse__MixedTags (line 97) | [Fact]
method TestParse__StringNull (line 109) | [Fact]
method TestParse__UrlConfigNull (line 120) | [Fact]
method TestParse__Empty (line 131) | [Fact]
method TestParse__StartsWithOpenBracket (line 142) | [Fact]
method TestParse__StartsWithColon (line 153) | [Fact]
method TestParse__EndsWithColon (line 164) | [Fact]
method TestParse__ClosingBracketInPrimaryKey (line 177) | [Fact]
method TestParse__PrimaryValueHasNoClosingBracket (line 188) | [Fact]
method TestParse__OpeningBracketInPrimaryTrailer (line 199) | [Fact]
method TestParse__ClosingBracketInPrimaryTrailer (line 210) | [Fact]
method TestParse__TagStartsWithOpenBracket (line 221) | [Fact]
method TestParse__TagStartsWithColon (line 232) | [Fact]
method TestParse__ClosingBracketInKey (line 246) | [Fact]
method TestParse__ValueHasNoClosingBracket (line 257) | [Fact]
method TestParse__OpeningBracketInTrailer (line 268) | [Fact]
method TestParse__ClosingBracketInTrailer (line 279) | [Fact]
FILE: ModuleManagerTests/Tags/TagListTest.cs
class TagListTest (line 7) | public class TagListTest
method TestPrimaryTag (line 9) | [Fact]
method TestEnumeration (line 18) | [Fact]
method TestConstructor__TagsNull (line 33) | [Fact]
FILE: ModuleManagerTests/Tags/TagTest.cs
class TagTest (line 7) | public class TagTest
method Test__OnlyKey (line 9) | [Fact]
method Test__KeyAndValue (line 19) | [Fact]
method Test__KeyAndEmptyValue (line 29) | [Fact]
method Test__KeyValueAndTrailer (line 39) | [Fact]
method Test__KeyEmptyValueAndTrailer (line 49) | [Fact]
method TestConstructor__KeyNull (line 59) | [Fact]
method TestConstructor__KeyEmpty (line 70) | [Fact]
method TestConstructor__ValueNullButTrailerNotNull (line 82) | [Fact]
method TestConstructor__TrailerEmpty (line 93) | [Fact]
method TestToString__Key (line 105) | [Fact]
method TestToString__KeyAndValue (line 113) | [Fact]
method TestToString__KeyAndEmptyValue (line 121) | [Fact]
method TestToString__KeyValueAndTrailer (line 129) | [Fact]
method TestToString__KeyEmptyValueAndTrailer (line 137) | [Fact]
FILE: ModuleManagerTests/Threading/BackgroundTaskTest.cs
class BackgroundTaskTest (line 7) | public class BackgroundTaskTest
method Test__Start (line 9) | [Fact]
method Test__Start__Exception (line 35) | [Fact]
method Test__Run__ArgumentNull (line 63) | [Fact]
FILE: ModuleManagerTests/Threading/TaskStatusTest.cs
class TaskStatusTest (line 7) | public class TaskStatusTest
method Test__Cosntructor (line 9) | [Fact]
method TestFinished (line 20) | [Fact]
method TestError (line 33) | [Fact]
method TestFinished__AlreadyFinished (line 47) | [Fact]
method TestFinished__AlreadyErrored (line 65) | [Fact]
method TestError__AlreadyFinished (line 84) | [Fact]
method TestError__AlreadyErrored (line 102) | [Fact]
FILE: ModuleManagerTests/Utils/CounterTest.cs
class CounterTest (line 10) | public class CounterTest
method Test__Constructor (line 12) | [Fact]
method TestIncrement (line 20) | [Fact]
method TestToString (line 36) | [Fact]
method Test__CastAsInt (line 52) | [Fact]
FILE: TestUtils/TestConfigNode.cs
class TestConfigNode (line 6) | public class TestConfigNode : ConfigNode, IEnumerable
method TestConfigNode (line 8) | public TestConfigNode() : base() { }
method TestConfigNode (line 9) | public TestConfigNode(string name) : base(name) { }
method Add (line 11) | public void Add(string name, string value) => Add(new Value(name, valu...
method Add (line 12) | public void Add(Value value) => values.Add(value);
method Add (line 13) | public void Add(string name, ConfigNode node) => AddNode(name, node);
method Add (line 14) | public void Add(ConfigNode node) => AddNode(node);
method GetEnumerator (line 16) | public IEnumerator GetEnumerator() => throw new NotImplementedExceptio...
FILE: TestUtils/UrlBuilder.cs
class UrlBuilder (line 8) | public static class UrlBuilder
method UrlBuilder (line 19) | static UrlBuilder()
method CreateRoot (line 40) | public static UrlDir CreateRoot()
method CreateDir (line 45) | public static UrlDir CreateDir(string url, UrlDir parent = null)
method CreateGameData (line 73) | public static UrlDir CreateGameData(UrlDir root = null)
method CreateFile (line 96) | public static UrlDir.UrlFile CreateFile(string path, UrlDir parent = n...
method CreateConfig (line 175) | public static UrlDir.UrlConfig CreateConfig(ConfigNode node, UrlDir.Ur...
method CreateConfig (line 182) | public static UrlDir.UrlConfig CreateConfig(string url, ConfigNode nod...
FILE: TestUtilsTests/DummyTest.cs
class DummyTest (line 6) | public class DummyTest
method PassingTest (line 8) | [Fact]
FILE: TestUtilsTests/TestConfigNodeTest.cs
class TestConfigNodeTest (line 7) | public class TestConfigNodeTest
method TestTestConfigNode (line 9) | [Fact]
method AssertValue (line 69) | private void AssertValue(string name, string value, ConfigNode.Value n...
FILE: TestUtilsTests/UrlBuilderTest.cs
class UrlBuilderTest (line 7) | public class UrlBuilderTest
method TestCreateRoot (line 9) | [Fact]
method TestCreateDir (line 19) | [Fact]
method TestCreateDir__Parent (line 36) | [Fact]
method TestCreateDir__Url (line 62) | [Fact]
method TestCreateDir__Url__Parent (line 89) | [Fact]
method TestCreateDir__Url__AlreadyExists (line 115) | [Fact]
method TestCreateGameData (line 138) | [Fact]
method TestCreateGameData__SpecifyRoot (line 154) | [Fact]
method TestCreateGameData__SpecifyRoot__GameDataAlreadyExists (line 167) | [Fact]
method TestCreateFile (line 177) | [Fact]
method TestCreateFile__Parent (line 194) | [Fact]
method TestCreateFile__Extension (line 212) | [InlineData("dll", UrlDir.FileType.Assembly)]
method TestCreateFile__Url (line 245) | [Fact]
method TestCreateFile__Url__AlreadyExists (line 280) | [Fact]
method TestCreateConfig (line 305) | [Fact]
method TestCreateConfig__Url (line 324) | [Fact]
method TestCreateConfig__Url__FileAlreadyExists (line 365) | [Fact]
Condensed preview — 175 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (675K chars).
[
{
"path": ".gitignore",
"chars": 1568,
"preview": "# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)\n[Oo]bj/\n\n# mstest test results\nTestResults\n\n## "
},
{
"path": "CopyLocalFalse.txt",
"chars": 268,
"preview": "Copy Local has been set to false for all dependencies.\nRemark by Jan Van der Haegen: This file has been added because N"
},
{
"path": "Makefile",
"chars": 1532,
"preview": "# Makefile for building ModuleManager\n\nKSPDIR := ${HOME}/.local/share/Steam/SteamApps/common/Kerbal\\ Space\\ Program\nMAN"
},
{
"path": "ModuleManager/Cats/CatAnimator.cs",
"chars": 993,
"preview": "using System.Collections;\nusing System.Diagnostics.CodeAnalysis;\nusing UnityEngine;\n\nnamespace ModuleManager.Cats\n{\n "
},
{
"path": "ModuleManager/Cats/CatManager.cs",
"chars": 4114,
"preview": "using System;\nusing UnityEngine;\n\nnamespace ModuleManager.Cats\n{\n public static class CatManager\n {\n priva"
},
{
"path": "ModuleManager/Cats/CatMover.cs",
"chars": 2348,
"preview": "using System.Diagnostics.CodeAnalysis;\nusing UnityEngine;\n\nnamespace ModuleManager.Cats\n{\n public class CatMover : M"
},
{
"path": "ModuleManager/Cats/CatOrbiter.cs",
"chars": 5001,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing KSP.UI;\nusing UnityEngine;"
},
{
"path": "ModuleManager/Collections/ArrayEnumerator.cs",
"chars": 1950,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace ModuleManager.Collections\n{\n pu"
},
{
"path": "ModuleManager/Collections/ImmutableStack.cs",
"chars": 2319,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace ModuleManager.Collections\n{\n pu"
},
{
"path": "ModuleManager/Collections/KeyValueCache.cs",
"chars": 850,
"preview": "using System;\nusing System.Collections.Generic;\n\nnamespace ModuleManager.Collections\n{\n public class KeyValueCache<TK"
},
{
"path": "ModuleManager/Collections/MessageQueue.cs",
"chars": 2374,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\n\nnamespace ModuleManager.Collections\n{\n pu"
},
{
"path": "ModuleManager/Command.cs",
"chars": 228,
"preview": "using System;\n\nnamespace ModuleManager\n{\n public enum Command\n {\n Insert,\n\n Delete,\n\n Edit,\n"
},
{
"path": "ModuleManager/CommandParser.cs",
"chars": 1400,
"preview": "using System;\n\nnamespace ModuleManager\n{\n public static class CommandParser\n {\n public static Command Pars"
},
{
"path": "ModuleManager/CustomConfigsManager.cs",
"chars": 702,
"preview": "using System;\nusing System.IO;\nusing UnityEngine;\n\nusing static ModuleManager.FilePathRepository;\n\nnamespace ModuleMana"
},
{
"path": "ModuleManager/ExceptionIntercept/InterceptLogHandler.cs",
"chars": 2476,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing Unit"
},
{
"path": "ModuleManager/Extensions/ByteArrayExtensions.cs",
"chars": 751,
"preview": "using System;\n\nnamespace ModuleManager.Extensions\n{\n public static class ByteArrayExtensions\n {\n public st"
},
{
"path": "ModuleManager/Extensions/ConfigNodeExtensions.cs",
"chars": 3681,
"preview": "using System;\nusing System.Text;\n\nnamespace ModuleManager.Extensions\n{\n public static class ConfigNodeExtensions\n "
},
{
"path": "ModuleManager/Extensions/IBasicLoggerExtensions.cs",
"chars": 1215,
"preview": "using System;\nusing UnityEngine;\nusing ModuleManager.Logging;\n\nnamespace ModuleManager.Extensions\n{\n public static c"
},
{
"path": "ModuleManager/Extensions/NodeStackExtensions.cs",
"chars": 699,
"preview": "using System;\nusing System.Linq;\nusing System.Text;\nusing NodeStack = ModuleManager.Collections.ImmutableStack<ConfigNo"
},
{
"path": "ModuleManager/Extensions/StringExtensions.cs",
"chars": 1065,
"preview": "using System;\nusing System.Text.RegularExpressions;\n\nnamespace ModuleManager.Extensions\n{\n public static class Strin"
},
{
"path": "ModuleManager/Extensions/UrlConfigExtensions.cs",
"chars": 1422,
"preview": "using System;\nusing System.Text;\n\nnamespace ModuleManager.Extensions\n{\n public static class UrlConfigExtensions\n "
},
{
"path": "ModuleManager/Extensions/UrlDirExtensions.cs",
"chars": 1375,
"preview": "using System;\nusing System.Linq;\n\nnamespace ModuleManager.Extensions\n{\n public static class UrlDirExtensions\n {\n "
},
{
"path": "ModuleManager/Extensions/UrlFileExtensions.cs",
"chars": 429,
"preview": "using System;\n\nnamespace ModuleManager.Extensions\n{\n public static class UrlFileExtensions\n {\n public stat"
},
{
"path": "ModuleManager/FatalErrorHandler.cs",
"chars": 1424,
"preview": "using System;\nusing UnityEngine;\n\nnamespace ModuleManager\n{\n public static class FatalErrorHandler\n {\n pub"
},
{
"path": "ModuleManager/FilePathRepository.cs",
"chars": 1428,
"preview": "using System;\nusing System.IO;\n\nnamespace ModuleManager\n{\n internal static class FilePathRepository\n {\n in"
},
{
"path": "ModuleManager/Fix16.cs",
"chars": 2055,
"preview": "using System.Collections;\nusing System.Diagnostics.CodeAnalysis;\n\nnamespace ModuleManager\n{\n class Fix16 : LoadingSy"
},
{
"path": "ModuleManager/Logging/IBasicLogger.cs",
"chars": 189,
"preview": "using System;\n\nnamespace ModuleManager.Logging\n{\n // Stripped down version of UnityEngine.ILogger\n public interfa"
},
{
"path": "ModuleManager/Logging/ILogMessage.cs",
"chars": 247,
"preview": "using System;\nusing UnityEngine;\n\nnamespace ModuleManager.Logging\n{\n public interface ILogMessage\n {\n LogT"
},
{
"path": "ModuleManager/Logging/LogMessage.cs",
"chars": 1681,
"preview": "using System;\nusing UnityEngine;\n\nnamespace ModuleManager.Logging\n{\n public class LogMessage : ILogMessage\n {\n "
},
{
"path": "ModuleManager/Logging/LogSplitter.cs",
"chars": 686,
"preview": "using System;\n\nnamespace ModuleManager.Logging\n{\n public class LogSplitter : IBasicLogger\n {\n private reado"
},
{
"path": "ModuleManager/Logging/PrefixLogger.cs",
"chars": 726,
"preview": "using System;\n\nnamespace ModuleManager.Logging\n{\n public class PrefixLogger : IBasicLogger\n {\n private rea"
},
{
"path": "ModuleManager/Logging/QueueLogRunner.cs",
"chars": 2182,
"preview": "using System;\nusing System.IO;\n\nusing ModuleManager.Collections;\n\nnamespace ModuleManager.Logging\n{\n public class Que"
},
{
"path": "ModuleManager/Logging/QueueLogger.cs",
"chars": 545,
"preview": "using System;\nusing ModuleManager.Collections;\n\nnamespace ModuleManager.Logging\n{\n public class QueueLogger : IBasic"
},
{
"path": "ModuleManager/Logging/StreamLogger.cs",
"chars": 854,
"preview": "using System;\nusing System.IO;\n\nnamespace ModuleManager.Logging\n{\n public sealed class StreamLogger : IBasicLogger, "
},
{
"path": "ModuleManager/Logging/UnityLogger.cs",
"chars": 524,
"preview": "using System;\nusing UnityEngine;\n\nnamespace ModuleManager.Logging\n{\n public class UnityLogger : IBasicLogger\n {\n "
},
{
"path": "ModuleManager/MMPatchLoader.cs",
"chars": 73820,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagnostics.CodeAnalysis;\nusing S"
},
{
"path": "ModuleManager/MMPatchRunner.cs",
"chars": 3627,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.IO;\nusing ModuleManager.Collecti"
},
{
"path": "ModuleManager/ModListGenerator.cs",
"chars": 10077,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Diagnostics;\nusing S"
},
{
"path": "ModuleManager/ModuleManager.cs",
"chars": 24848,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Diagno"
},
{
"path": "ModuleManager/ModuleManager.csproj",
"chars": 9274,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project DefaultTargets=\"Build\" ToolsVersion=\"12.0\" xmlns=\"http://schemas.micros"
},
{
"path": "ModuleManager/ModuleManagerTestRunner.cs",
"chars": 3018,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing ModuleManager.Extensions;\nu"
},
{
"path": "ModuleManager/NeedsChecker.cs",
"chars": 7084,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics.CodeAnalysis;\nusing System.Linq;\nusing ModuleM"
},
{
"path": "ModuleManager/NodeMatcher.cs",
"chars": 1912,
"preview": "using System;\nusing ModuleManager.Extensions;\n\nnamespace ModuleManager\n{\n public interface INodeMatcher\n {\n "
},
{
"path": "ModuleManager/Operator.cs",
"chars": 206,
"preview": "using System;\n\nnamespace ModuleManager\n{\n public enum Operator\n {\n Assign,\n Add,\n Subtract,\n"
},
{
"path": "ModuleManager/OperatorParser.cs",
"chars": 1511,
"preview": "using System;\n\nnamespace ModuleManager\n{\n public static class OperatorParser\n {\n public static Operator Pa"
},
{
"path": "ModuleManager/Pass.cs",
"chars": 940,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing ModuleManager.Patches;\n\nnamespace Modul"
},
{
"path": "ModuleManager/PatchApplier.cs",
"chars": 1760,
"preview": "using System;\nusing System.Collections.Generic;\nusing ModuleManager.Logging;\nusing ModuleManager.Extensions;\nusing Modu"
},
{
"path": "ModuleManager/PatchContext.cs",
"chars": 725,
"preview": "using System;\nusing System.Collections.Generic;\nusing ModuleManager.Logging;\nusing ModuleManager.Progress;\n\nnamespace M"
},
{
"path": "ModuleManager/PatchExtractor.cs",
"chars": 4816,
"preview": "using System;\nusing System.Diagnostics.CodeAnalysis;\nusing ModuleManager.Extensions;\nusing ModuleManager.Logging;\nusing"
},
{
"path": "ModuleManager/PatchList.cs",
"chars": 5847,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing ModuleManager.Patches;\nusing ModuleMana"
},
{
"path": "ModuleManager/Patches/CopyPatch.cs",
"chars": 2754,
"preview": "using System;\nusing System.Collections.Generic;\nusing NodeStack = ModuleManager.Collections.ImmutableStack<ConfigNode>;"
},
{
"path": "ModuleManager/Patches/DeletePatch.cs",
"chars": 2135,
"preview": "using System;\nusing System.Collections.Generic;\nusing ModuleManager.Extensions;\nusing ModuleManager.Logging;\nusing Modu"
},
{
"path": "ModuleManager/Patches/EditPatch.cs",
"chars": 2674,
"preview": "using System;\nusing System.Collections.Generic;\nusing NodeStack = ModuleManager.Collections.ImmutableStack<ConfigNode>;"
},
{
"path": "ModuleManager/Patches/IPatch.cs",
"chars": 458,
"preview": "using System;\nusing System.Collections.Generic;\nusing ModuleManager.Logging;\nusing ModuleManager.Patches.PassSpecifiers"
},
{
"path": "ModuleManager/Patches/InsertPatch.cs",
"chars": 1404,
"preview": "using System;\nusing System.Collections.Generic;\nusing ModuleManager.Extensions;\nusing ModuleManager.Logging;\nusing Modu"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/AfterPassSpecifier.cs",
"chars": 1111,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public class AfterPas"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/BeforePassSpecifier.cs",
"chars": 1115,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public class BeforePa"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/FinalPassSpecifier.cs",
"chars": 516,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public class FinalPas"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/FirstPassSpecifier.cs",
"chars": 516,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public class FirstPas"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/ForPassSpecifier.cs",
"chars": 1103,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public class ForPassS"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/IPassSpecifier.cs",
"chars": 258,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public interface IPas"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/InsertPassSpecifier.cs",
"chars": 528,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public class InsertPa"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/LastPassSpecifier.cs",
"chars": 592,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public class LastPassS"
},
{
"path": "ModuleManager/Patches/PassSpecifiers/LegacyPassSpecifier.cs",
"chars": 528,
"preview": "using System;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Patches.PassSpecifiers\n{\n public class LegacyPa"
},
{
"path": "ModuleManager/Patches/PatchCompiler.cs",
"chars": 1224,
"preview": "using System;\n\nnamespace ModuleManager.Patches\n{\n public interface IPatchCompiler\n {\n IPatch CompilePatch("
},
{
"path": "ModuleManager/Patches/ProtoPatch.cs",
"chars": 895,
"preview": "using System;\nusing ModuleManager.Patches.PassSpecifiers;\n\nnamespace ModuleManager.Patches\n{\n public class ProtoPatc"
},
{
"path": "ModuleManager/Patches/ProtoPatchBuilder.cs",
"chars": 10476,
"preview": "using System;\nusing ModuleManager.Extensions;\nusing ModuleManager.Patches.PassSpecifiers;\nusing ModuleManager.Progress;"
},
{
"path": "ModuleManager/PostPatchLoader.cs",
"chars": 7881,
"preview": "using System;\nusing System.IO;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nus"
},
{
"path": "ModuleManager/Progress/IPatchProgress.cs",
"chars": 1288,
"preview": "using System;\n\nnamespace ModuleManager.Progress\n{\n public interface IPatchProgress\n {\n ProgressCounter Cou"
},
{
"path": "ModuleManager/Progress/PatchProgress.cs",
"chars": 5347,
"preview": "using System;\nusing ModuleManager.Extensions;\nusing ModuleManager.Logging;\n\nnamespace ModuleManager.Progress\n{\n publ"
},
{
"path": "ModuleManager/Progress/ProgressCounter.cs",
"chars": 774,
"preview": "using System;\nusing System.Collections.Generic;\nusing ModuleManager.Utils;\n\nnamespace ModuleManager.Progress\n{\n publ"
},
{
"path": "ModuleManager/Properties/AssemblyInfo.cs",
"chars": 1026,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\n\n// Information about this assembly is defined by the fo"
},
{
"path": "ModuleManager/Properties/Resources.Designer.cs",
"chars": 5792,
"preview": "//------------------------------------------------------------------------------\n// <auto-generated>\n// This code w"
},
{
"path": "ModuleManager/Properties/Resources.resx",
"chars": 8577,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<root>\n <!-- \n Microsoft ResX Schema \n \n Version 2.0\n \n The prim"
},
{
"path": "ModuleManager/ProtoUrlConfig.cs",
"chars": 1087,
"preview": "using System;\n\nnamespace ModuleManager\n{\n public interface IUrlConfigIdentifier\n {\n string FileUrl { get; "
},
{
"path": "ModuleManager/Tags/Tag.cs",
"chars": 1049,
"preview": "using System;\n\nnamespace ModuleManager.Tags\n{\n public struct Tag\n {\n public readonly string key;\n p"
},
{
"path": "ModuleManager/Tags/TagList.cs",
"chars": 845,
"preview": "using System;\nusing System.Collections;\nusing System.Collections.Generic;\nusing System.Linq;\nusing ModuleManager.Collec"
},
{
"path": "ModuleManager/Tags/TagListParser.cs",
"chars": 6490,
"preview": "using System;\nusing System.Collections.Generic;\nusing ModuleManager.Progress;\n\nnamespace ModuleManager.Tags\n{\n publi"
},
{
"path": "ModuleManager/Threading/BackgroundTask.cs",
"chars": 761,
"preview": "using System;\nusing System.Threading;\n\nnamespace ModuleManager.Threading\n{\n public static class BackgroundTask\n {"
},
{
"path": "ModuleManager/Threading/ITaskStatus.cs",
"chars": 241,
"preview": "using System;\n\nnamespace ModuleManager.Threading\n{\n public interface ITaskStatus\n {\n bool IsRunning { get;"
},
{
"path": "ModuleManager/Threading/TaskStatus.cs",
"chars": 1339,
"preview": "using System;\n\nnamespace ModuleManager.Threading\n{\n public class TaskStatus : ITaskStatus\n {\n private read"
},
{
"path": "ModuleManager/Threading/TaskStatusWrapper.cs",
"chars": 488,
"preview": "using System;\n\nnamespace ModuleManager.Threading\n{\n public class TaskStatusWrapper : ITaskStatus\n {\n priva"
},
{
"path": "ModuleManager/Utils/Counter.cs",
"chars": 393,
"preview": "using System;\n\nnamespace ModuleManager.Utils\n{\n public class Counter\n {\n public int Value { get; private s"
},
{
"path": "ModuleManager/Utils/FileUtils.cs",
"chars": 599,
"preview": "using System;\nusing System.IO;\nusing System.Security.Cryptography;\nusing ModuleManager.Extensions;\n\nnamespace ModuleMan"
},
{
"path": "ModuleManager/copy_build.sh",
"chars": 1357,
"preview": "if [ -z \"${TARGET_PATH}\" ] ; then\n echo 'Expected $TARGET_PATH to be defined but it is not' >&2\n exit 1\nelif ! [ -f \"$"
},
{
"path": "ModuleManager/packages.config",
"chars": 136,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"CopyLocalFalse\" version=\"1.0.2\" targetFramework=\"net35"
},
{
"path": "ModuleManager.sln",
"chars": 2759,
"preview": "\r\nMicrosoft Visual Studio Solution File, Format Version 12.00\r\n# Visual Studio 15\r\nVisualStudioVersion = 15.0.26730.12\r"
},
{
"path": "ModuleManager.sln.DotSettings",
"chars": 4502,
"preview": "<wpf:ResourceDictionary xml:space=\"preserve\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" xmlns:s=\"clr-namesp"
},
{
"path": "ModuleManagerTests/Collections/ArrayEnumeratorTest.cs",
"chars": 4858,
"preview": "using System;\nusing System.Collections.Generic;\nusing Xunit;\nusing ModuleManager.Collections;\n\nnamespace ModuleManagerT"
},
{
"path": "ModuleManagerTests/Collections/ImmutableStackTest.cs",
"chars": 4308,
"preview": "using System;\nusing System.Linq;\nusing Xunit;\n\nusing ModuleManager.Collections;\n\nnamespace ModuleManagerTests.Collectio"
},
{
"path": "ModuleManagerTests/Collections/KeyValueCacheTest.cs",
"chars": 1484,
"preview": "using System;\nusing Xunit;\nusing ModuleManager.Collections;\n\nnamespace ModuleManagerTests.Collections\n{\n public class"
},
{
"path": "ModuleManagerTests/Collections/MessageQueueTest.cs",
"chars": 1280,
"preview": "using System;\nusing Xunit;\nusing ModuleManager.Collections;\n\nnamespace ModuleManagerTests.Collections\n{\n public clas"
},
{
"path": "ModuleManagerTests/CommandParserTest.cs",
"chars": 2595,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Xunit;\nusing ModuleManager;"
},
{
"path": "ModuleManagerTests/DummyTest.cs",
"chars": 201,
"preview": "using System;\nusing Xunit;\n\nnamespace ModuleManagerTests\n{\n public class DummyTest\n {\n [Fact]\n publ"
},
{
"path": "ModuleManagerTests/Extensions/ByteArrayExtensionsTest.cs",
"chars": 647,
"preview": "using System;\nusing Xunit;\nusing ModuleManager.Extensions;\n\nnamespace ModuleManagerTests.Extensions\n{\n public class "
},
{
"path": "ModuleManagerTests/Extensions/ConfigNodeExtensionsTest.cs",
"chars": 10959,
"preview": "using System;\nusing System.Text;\nusing Xunit;\nusing TestUtils;\nusing ModuleManager.Extensions;\n\nnamespace ModuleManager"
},
{
"path": "ModuleManagerTests/Extensions/IBasicLoggerExtensionsTest.cs",
"chars": 2145,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager.Logging;\nusing ModuleManager.Extensions;\n\nnamespace M"
},
{
"path": "ModuleManagerTests/Extensions/NodeStackExtensionsTest.cs",
"chars": 604,
"preview": "using System;\nusing Xunit;\nusing ModuleManager.Collections;\nusing ModuleManager.Extensions;\n\nnamespace ModuleManagerTes"
},
{
"path": "ModuleManagerTests/Extensions/StringExtensionsTest.cs",
"chars": 1962,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Xunit;\nusing ModuleManager."
},
{
"path": "ModuleManagerTests/Extensions/UrlConfigExtensionsTest.cs",
"chars": 5612,
"preview": "using System;\nusing Xunit;\nusing TestUtils;\nusing ModuleManager.Extensions;\n\nnamespace ModuleManagerTests.Extensions\n{\n"
},
{
"path": "ModuleManagerTests/Extensions/UrlDirExtensionsTest.cs",
"chars": 2486,
"preview": "using System;\nusing Xunit;\nusing TestUtils;\nusing ModuleManager.Extensions;\n\nnamespace ModuleManagerTests.Extensions\n{\n"
},
{
"path": "ModuleManagerTests/Extensions/UrlFileExtensionsTest.cs",
"chars": 423,
"preview": "using System;\nusing Xunit;\nusing TestUtils;\nusing ModuleManager.Extensions;\n\nnamespace ModuleManagerTests.Extensions\n{\n"
},
{
"path": "ModuleManagerTests/InGameTestRunnerTest.cs",
"chars": 5938,
"preview": "using System;\nusing System.Linq;\nusing Xunit;\nusing NSubstitute;\nusing UnityEngine;\nusing TestUtils;\nusing ModuleManage"
},
{
"path": "ModuleManagerTests/Logging/LogMessageTest.cs",
"chars": 4595,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing UnityEngine;\nusing ModuleManager.Logging;\n\nnamespace ModuleManagerT"
},
{
"path": "ModuleManagerTests/Logging/LogSplitterTest.cs",
"chars": 1700,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing UnityEngine;\nusing ModuleManager.Logging;\n\nnamespace ModuleManagerTe"
},
{
"path": "ModuleManagerTests/Logging/PrefixLoggerTest.cs",
"chars": 2212,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing UnityEngine;\nusing ModuleManager.Logging;\n\nnamespace ModuleManagerT"
},
{
"path": "ModuleManagerTests/Logging/QueueLogRunnerTest.cs",
"chars": 5736,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\n\nusing ModuleManager.Collections;\nusing ModuleManager.Logging;\n\nnamespace "
},
{
"path": "ModuleManagerTests/Logging/QueueLoggerTest.cs",
"chars": 1231,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager.Collections;\nusing ModuleManager.Logging;\n\nnamespace "
},
{
"path": "ModuleManagerTests/Logging/StreamLoggerTest.cs",
"chars": 2636,
"preview": "using System;\nusing System.IO;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager.Logging;\n\nnamespace ModuleManagerTes"
},
{
"path": "ModuleManagerTests/Logging/UnityLoggerTest.cs",
"chars": 1389,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing UnityEngine;\nusing ModuleManager.Extensions;\nusing ModuleManager.Lo"
},
{
"path": "ModuleManagerTests/LoggingAssertionHelpers.cs",
"chars": 3072,
"preview": "using System;\nusing UnityEngine;\nusing NSubstitute;\nusing ModuleManager.Logging;\n\nnamespace ModuleManagerTests\n{\n pu"
},
{
"path": "ModuleManagerTests/MMPatchLoaderTest.cs",
"chars": 5499,
"preview": "using System;\nusing System.Linq;\nusing Xunit;\nusing NSubstitute;\nusing UnityEngine;\nusing TestUtils;\nusing ModuleManage"
},
{
"path": "ModuleManagerTests/ModuleManagerTests.csproj",
"chars": 10314,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
},
{
"path": "ModuleManagerTests/NeedsCheckerTest.cs",
"chars": 14892,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager;\nusing ModuleManager.Logging;\nusing "
},
{
"path": "ModuleManagerTests/NodeMatcherTest.cs",
"chars": 9375,
"preview": "using System;\nusing Xunit;\nusing TestUtils;\nusing ModuleManager;\n\nnamespace ModuleManagerTests\n{\n public class NodeM"
},
{
"path": "ModuleManagerTests/OperatorParserTest.cs",
"chars": 3085,
"preview": "using System;\nusing Xunit;\nusing ModuleManager;\n\nnamespace ModuleManagerTests\n{\n public class OperatorParserTest\n "
},
{
"path": "ModuleManagerTests/PassTest.cs",
"chars": 1656,
"preview": "using System;\nusing System.Linq;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager;\nusing ModuleManager.Patches;\n\nnam"
},
{
"path": "ModuleManagerTests/PatchApplierTest.cs",
"chars": 4648,
"preview": "using System;\nusing System.Collections.Generic;\nusing Xunit;\nusing NSubstitute;\nusing UnityEngine;\nusing ModuleManager;"
},
{
"path": "ModuleManagerTests/PatchExtractorTest.cs",
"chars": 14320,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager;\nusing ModuleManager.Logging;\nusing "
},
{
"path": "ModuleManagerTests/PatchListTest.cs",
"chars": 9572,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusi"
},
{
"path": "ModuleManagerTests/Patches/CopyPatchTest.cs",
"chars": 12896,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusi"
},
{
"path": "ModuleManagerTests/Patches/DeletePatchTest.cs",
"chars": 6741,
"preview": "using System;\nusing System.Collections.Generic;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager;\nu"
},
{
"path": "ModuleManagerTests/Patches/EditPatchTest.cs",
"chars": 11219,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\nusing NSubstitute;\nusing UnityEngine;\nu"
},
{
"path": "ModuleManagerTests/Patches/InsertPatchTest.cs",
"chars": 7409,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusi"
},
{
"path": "ModuleManagerTests/Patches/PassSpecifiers/AfterPassSpecifierTest.cs",
"chars": 3125,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager;\nusing ModuleManager.Patches.PassSpe"
},
{
"path": "ModuleManagerTests/Patches/PassSpecifiers/BeforePassSpecifierTest.cs",
"chars": 3151,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager;\nusing ModuleManager.Patches.PassSpe"
},
{
"path": "ModuleManagerTests/Patches/PassSpecifiers/FinalPassSpecifierTest.cs",
"chars": 1492,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager;\nusing ModuleManager.Patches.PassSpecifiers;\nusing Mo"
},
{
"path": "ModuleManagerTests/Patches/PassSpecifiers/FirstPassSpecifierTest.cs",
"chars": 1491,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager;\nusing ModuleManager.Patches.PassSpecifiers;\nusing Mo"
},
{
"path": "ModuleManagerTests/Patches/PassSpecifiers/ForPassSpecifierTest.cs",
"chars": 3103,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager;\nusing ModuleManager.Patches.PassSpe"
},
{
"path": "ModuleManagerTests/Patches/PassSpecifiers/InsertPassSpecifierTest.cs",
"chars": 1505,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager;\nusing ModuleManager.Patches.PassSpecifiers;\nusing Mo"
},
{
"path": "ModuleManagerTests/Patches/PassSpecifiers/LastPassSpecifierTest.cs",
"chars": 1558,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager;\nusing ModuleManager.Patches.PassSpecifiers;\nusing Mod"
},
{
"path": "ModuleManagerTests/Patches/PassSpecifiers/LegacyPassSpecifierTest.cs",
"chars": 1505,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing ModuleManager;\nusing ModuleManager.Patches.PassSpecifiers;\nusing Mo"
},
{
"path": "ModuleManagerTests/Patches/PatchCompilerTest.cs",
"chars": 11881,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusi"
},
{
"path": "ModuleManagerTests/Patches/ProtoPatchBuilderTest.cs",
"chars": 54842,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager;\nusing ModuleManager.Collections;\nus"
},
{
"path": "ModuleManagerTests/Progress/PatchProgressTest.cs",
"chars": 15949,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager;\nusing ModuleManager.Logging;\nusing "
},
{
"path": "ModuleManagerTests/Properties/AssemblyInfo.cs",
"chars": 1404,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "ModuleManagerTests/ProtoUrlConfigTest.cs",
"chars": 2562,
"preview": "using System;\nusing Xunit;\nusing TestUtils;\nusing ModuleManager;\n\nnamespace ModuleManagerTests\n{\n public class ProtoU"
},
{
"path": "ModuleManagerTests/Tags/TagListParserTest.cs",
"chars": 9258,
"preview": "using System;\nusing Xunit;\nusing NSubstitute;\nusing TestUtils;\nusing ModuleManager.Progress;\nusing ModuleManager.Tags;\n"
},
{
"path": "ModuleManagerTests/Tags/TagListTest.cs",
"chars": 1163,
"preview": "using System;\nusing Xunit;\nusing ModuleManager.Tags;\n\nnamespace ModuleManagerTests.Tags\n{\n public class TagListTest\n"
},
{
"path": "ModuleManagerTests/Tags/TagTest.cs",
"chars": 3829,
"preview": "using System;\nusing Xunit;\nusing ModuleManager.Tags;\n\nnamespace ModuleManagerTests.Tags\n{\n public class TagTest\n "
},
{
"path": "ModuleManagerTests/Threading/BackgroundTaskTest.cs",
"chars": 1851,
"preview": "using System;\nusing Xunit;\nusing ModuleManager.Threading;\n\nnamespace ModuleManagerTests.Threading\n{\n public class Ba"
},
{
"path": "ModuleManagerTests/Threading/TaskStatusTest.cs",
"chars": 3212,
"preview": "using System;\nusing Xunit;\nusing ModuleManager.Threading;\n\nnamespace ModuleManagerTests.Threading\n{\n public class Ta"
},
{
"path": "ModuleManagerTests/Utils/CounterTest.cs",
"chars": 1449,
"preview": "using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing Xunit;\nusing ModuleManager."
},
{
"path": "ModuleManagerTests/app.config",
"chars": 694,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n <runtime>\n <assemblyBinding xmlns=\"urn:schemas-microsoft-co"
},
{
"path": "ModuleManagerTests/packages.config",
"chars": 1165,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"Castle.Core\" version=\"4.4.1\" targetFramework=\"net471\" "
},
{
"path": "README.md",
"chars": 842,
"preview": "ModuleManager\n=============\n\n\nOriginal (c) from Ialdabaoth ( https://github.com/Ialdabaoth )\n\nModified by // Modificatio"
},
{
"path": "TestUtils/Properties/AssemblyInfo.cs",
"chars": 1386,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "TestUtils/TestConfigNode.cs",
"chars": 605,
"preview": "using System;\nusing System.Collections;\n\nnamespace TestUtils\n{\n public class TestConfigNode : ConfigNode, IEnumerabl"
},
{
"path": "TestUtils/TestUtils.csproj",
"chars": 2330,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
},
{
"path": "TestUtils/UrlBuilder.cs",
"chars": 6918,
"preview": "using System;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\n\nnamespace TestUtils\n{\n public static cla"
},
{
"path": "TestUtilsTests/DummyTest.cs",
"chars": 197,
"preview": "using System;\nusing Xunit;\n\nnamespace TestUtilsTests\n{\n public class DummyTest\n {\n [Fact]\n public v"
},
{
"path": "TestUtilsTests/Properties/AssemblyInfo.cs",
"chars": 1396,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "TestUtilsTests/TestConfigNodeTest.cs",
"chars": 2816,
"preview": "using System;\nusing Xunit;\nusing TestUtils;\n\nnamespace TestUtilsTests\n{\n public class TestConfigNodeTest\n {\n "
},
{
"path": "TestUtilsTests/TestUtilsTests.csproj",
"chars": 5990,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"15.0\" xmlns=\"http://schemas.microsoft.com/developer/msbui"
},
{
"path": "TestUtilsTests/UrlBuilderTest.cs",
"chars": 13882,
"preview": "using System;\nusing Xunit;\nusing TestUtils;\n\nnamespace TestUtilsTests\n{\n public class UrlBuilderTest\n {\n ["
},
{
"path": "TestUtilsTests/packages.config",
"chars": 828,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"xunit\" version=\"2.4.1\" targetFramework=\"net471\" />\n <"
},
{
"path": "Tests/AltEdits.cfg",
"chars": 424,
"preview": "\nMMTEST\n{\n\tname = altEdits\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n\tMODULE\n\t{\n\t\tname = module2\n\t}\n}\n\n// Adds value to module2\n@MM"
},
{
"path": "Tests/BadlyFormed.cfg",
"chars": 630,
"preview": "\nMMTEST\n{\n\tname = badFormat\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n\t// Brackets without name\n\t//MODULE\n\t{\n\t\tname = module2\n\t}\n\tt"
},
{
"path": "Tests/NameLessNode.cfg",
"chars": 216,
"preview": "MMTEST\n{\n\tname = NameLessNode\n}\n\n@MMTEST[NameLessNode]\n{\n\t// Insert a name Less node\n %MODEL {\n %key = value\n "
},
{
"path": "Tests/Needs.cfg",
"chars": 443,
"preview": "\nMMTEST\n{\n\tname = testNeeds\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n\tMODULE\n\t{\n\t\tname = module2\n\t}\n}\n\n// Adds value to module2\n@M"
},
{
"path": "Tests/NodeCopy.cfg",
"chars": 704,
"preview": "\nMMTEST\n{\n\tname = nodeCopy\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n}\n\n// Adds value to module2\n@MMTEST[nodeCopy]\n{\n\t// Copy modul"
},
{
"path": "Tests/NodeCreate.cfg",
"chars": 343,
"preview": "MMTEST\n{\n\tname = CreateNode\n\tNODE\n\t{\n\t\tname = TestNode1\n\t\t\n\t\tvalue = test1\n\t}\n}\n\n@MMTEST[CreateNode]\n{\n\t&NODE[TestNode1]"
},
{
"path": "Tests/NodeDelete.cfg",
"chars": 786,
"preview": "\nMMTEST\n{\n\tname = nodeDelete\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n\tMODULE\n\t{\n\t\tname = module2\n\t\tkey = copy0\n\t}\n\tMODULE\n\t{\t\n\t\tn"
},
{
"path": "Tests/NodeEdit.cfg",
"chars": 770,
"preview": "\nMMTEST\n{\n\tname = nodeEdit\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n\tMODULE\n\t{\n\t\tname = module2\n\t\tkey = firstCopy\n\t}\n\tMODULE\n\t{\n\t\t"
},
{
"path": "Tests/NodeEditWildcard.cfg",
"chars": 768,
"preview": "\nMMTEST\n{\n\tname = nodeEditWildcard\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n\tMODULE\n\t{\n\t\tname = module2\n\t\tkey = firstCopy\n\t}\n\tMODU"
},
{
"path": "Tests/NodeHas.cfg",
"chars": 756,
"preview": "\nMMTEST\n{\n\tname = nodeHas\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n\tMODULE\n\t{\n\t\tname = module2\n\t\tkey = firstCopy\n\t\tvalue = 5\n\t}\n\tM"
},
{
"path": "Tests/NodeInsert.cfg",
"chars": 472,
"preview": "\nMMTEST\n{\n\tname = nodeInsert\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n}\n\n// Adds value to module2\n@MMTEST[nodeInsert]\n{\n\t// Added "
},
{
"path": "Tests/NodeReplace.cfg",
"chars": 755,
"preview": "\nMMTEST\n{\n\tname = nodeReplace\n\tMODULE\n\t{\n\t\tname = module1\n\t}\n\tMODULE\n\t{\n\t\tname = module2\n\t\tkey = firstCopy\n\t}\n\tMODULE\n\t{"
},
{
"path": "Tests/ValueCopy.cfg",
"chars": 526,
"preview": "\nMMTEST\n{\n\tname = valueCopy\n\tMODULE\n\t{\n\t\tname = module1\n\t\tmultiVal = one\n\t\tmultiVal = two\n\t\tnumeric = 0\n\t}\n}\n\n// Adds va"
},
{
"path": "Tests/ValueCreate.cfg",
"chars": 429,
"preview": "MMTEST\n{\n\tname = valueCreate\n\tvalue1 = test1\n\tvalue2 = test2\n\tNODE\n\t{\n\t\tname = testNode\n\t\t\n\t\tvalue3 = test3\n\t\tvalue4 = t"
},
{
"path": "Tests/ValueDelete.cfg",
"chars": 469,
"preview": "\nMMTEST\n{\n\tname = valueDelete\n\tMODULE\n\t{\n\t\tname = module1\n\t\tmultiVal = one\n\t\tmultiVal = two\n\t\tmultiVal2 = one\n\t\tmultiVal"
},
{
"path": "Tests/ValueEdit.cfg",
"chars": 545,
"preview": "\nMMTEST\n{\n\tname = valueEdit\n\tMODULE\n\t{\n\t\tname = module1\n\t\tmultiVal = one\n\t\tmultiVal = two\n\t\tnumeric = 0\n\t}\n}\n\n// Adds va"
},
{
"path": "Tests/ValueEmpty.cfg",
"chars": 299,
"preview": "\nMMTEST\n{\n\tname = valueEmpty\n\tMODULE\n\t{\n\t\tname = module1\n\t\temptyVal = notEmptyYet\n\t}\n}\n\n// Changes value to empty\n@MMTES"
},
{
"path": "Tests/ValueInsert.cfg",
"chars": 517,
"preview": "\nMMTEST\n{\n\tname = valueInsert\n\tMODULE\n\t{\n\t\tname = module1\n\t\tmultiVal = one\n\t\tmultiVal = two\n\t\tnumeric = 0\n\t}\n}\n\n// Adds "
},
{
"path": "Tests/ValueReplace.cfg",
"chars": 499,
"preview": "\nMMTEST\n{\n\tname = valueReplace\n\tMODULE\n\t{\n\t\tname = module1\n\t\tmultiVal = one\n\t\tmultiVal = two\n\t\tnumeric = 0\n\t}\n}\n\n// Adds"
}
]
About this extraction
This page contains the full source code of the sarbian/ModuleManager GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 175 files (620.3 KB), approximately 136.8k tokens, and a symbol index with 959 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.