Skip to content

Commit a7c66ca

Browse files
committed
Default step pipeline and injectors
1 parent 7cb3c10 commit a7c66ca

26 files changed

+682
-195
lines changed

CHANGELOG.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1-
# 1.0.0
1+
# 1.1.0
2+
3+
- Step pipeline workflow
4+
- Default steps in pipeline
5+
- Basic step list validator
6+
- Final steps list printing via window button
7+
- Slightly faster project scrubbing
8+
9+
# 1.0.0
210

311
- Initial version

Editor/AssetScrubber.cs

Lines changed: 196 additions & 101 deletions
Large diffs are not rendered by default.

Editor/PatcherSteps.cs

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,41 +12,64 @@ namespace Nomnom.UnityProjectPatcher.Editor {
1212
public static class PatcherSteps {
1313
[InitializeOnLoadMethod]
1414
private static void OnLoad() {
15-
// locate game patcher
16-
var gameWrapperType = PatcherUtility.GetGameWrapperType();
17-
if (gameWrapperType is null) return;
15+
var progress = StepsProgress.FromPath(StepsProgress.SavePath);
16+
if (progress is null) return;
1817

19-
var runFunction = PatcherUtility.GetGameWrapperRunFunction(gameWrapperType);
20-
if (runFunction is null) return;
21-
22-
StartDelay(runFunction);
23-
24-
// if (!InternalEditorUtility.isApplicationActive) {
25-
// EditorApplication.update -= Update;
26-
// EditorApplication.update += Update;
27-
// RunFunction(runFunction);
28-
// } else {
29-
// StartDelay(runFunction);
30-
// }
18+
Run();
3119
}
3220

33-
// private static void Update() {
34-
// if (!InternalEditorUtility.isApplicationActive) {
35-
// EditorApplication.delayCall?.Invoke();
36-
// }
37-
// }
38-
39-
private static void StartDelay(MethodInfo runFunction) {
21+
private static void StartDelay(MethodInfo func) {
4022
EditorApplication.delayCall += () => {
41-
RunFunction(runFunction);
23+
RunFunction(func);
4224
};
4325
}
4426

45-
private static void RunFunction(MethodInfo runFunction) {
46-
var progress = StepsProgress.FromPath(StepsProgress.SavePath);
47-
if (progress is null) return;
27+
public static void Run() {
28+
// locate game patcher
29+
var gameWrapperType = PatcherUtility.GetGameWrapperType();
30+
if (gameWrapperType is null) {
31+
Debug.LogError("Could not find GameWrapper type");
32+
return;
33+
}
34+
35+
var func = PatcherUtility.GetGameWrapperGetStepsFunction(gameWrapperType);
36+
if (func is null) {
37+
Debug.LogError("Could not find GameWrapper.GetSteps function");
38+
return;
39+
}
40+
41+
StartDelay(func);
42+
}
43+
44+
private static void RunFunction(MethodInfo func) {
45+
var pipeline = new StepPipeline();
46+
func.Invoke(null, new object[] { pipeline });
47+
if (!pipeline.Validate()) {
48+
Debug.LogError("Pipeline validation failed");
49+
return;
50+
}
51+
52+
var executor = new StepsExecutor(pipeline.Steps.ToArray());
53+
executor.Execute();
54+
}
4855

49-
runFunction.Invoke(null, null);
56+
public static StepPipeline GetPipeline() {
57+
var pipeline = new StepPipeline();
58+
59+
var gameWrapperType = PatcherUtility.GetGameWrapperType();
60+
if (gameWrapperType is null) {
61+
return pipeline;
62+
}
63+
64+
var func = PatcherUtility.GetGameWrapperGetStepsFunction(gameWrapperType);
65+
if (func is null) {
66+
return pipeline;
67+
}
68+
69+
func.Invoke(null, new object[] { pipeline });
70+
if (!pipeline.Validate()) return null;
71+
72+
return pipeline;
5073
}
5174
}
5275
}

Editor/PatcherUtility.cs

Lines changed: 96 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
using System.Reflection;
66
using System.Text;
77
using System.Text.RegularExpressions;
8+
using System.Threading;
9+
using System.Threading.Tasks;
810
using Lachee.Utilities.Serialization;
911
using Nomnom.UnityProjectPatcher.AssetRipper;
1012
using Nomnom.UnityProjectPatcher.Editor.Steps;
@@ -13,6 +15,7 @@
1315
using UnityEditor.SceneManagement;
1416
using UnityEditorInternal;
1517
using UnityEngine;
18+
using UnityEngine.Profiling;
1619
using Object = UnityEngine.Object;
1720
using UObject = Lachee.Utilities.Serialization.UObject;
1821

@@ -71,19 +74,32 @@ public static bool ScriptsAreStubs(this IPatcherStep step) {
7174
public static Type GetGameWrapperType() {
7275
foreach (var type in TypeCache.GetTypesWithAttribute<UPPatcherAttribute>()) {
7376
// does it have a Run function?
74-
if (GetGameWrapperRunFunction(type) is null) continue;
77+
if (GetGameWrapperGetStepsFunction(type) is null) continue;
7578
return type;
7679
}
7780

7881
return null;
7982
}
8083

81-
public static MethodInfo GetGameWrapperRunFunction(Type wrapperType) {
82-
var runFunction = wrapperType.GetMethod("Run", BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
83-
if (runFunction is null) return null;
84-
if (runFunction.GetParameters().Length > 0) return null;
84+
// public static MethodInfo GetGameWrapperRunFunction(Type wrapperType) {
85+
// var runFunction = wrapperType.GetMethod("Run", BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
86+
// if (runFunction is null) return null;
87+
// if (runFunction.GetParameters().Length > 0) return null;
88+
//
89+
// return runFunction;
90+
// }
91+
92+
public static MethodInfo GetGameWrapperGetStepsFunction(Type wrapperType) {
93+
var getStepsFunction = wrapperType.GetMethod("GetSteps", BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
94+
if (getStepsFunction is null) return null;
95+
96+
var parameters = getStepsFunction.GetParameters();
97+
if (parameters.Length != 1) return null;
98+
if (parameters[0].ParameterType != typeof(StepPipeline)) {
99+
return null;
100+
}
85101

86-
return runFunction;
102+
return getStepsFunction;
87103
}
88104

89105
public static MethodInfo GetGameWrapperOnGUIFunction(Type wrapperType) {
@@ -199,6 +215,17 @@ public static string GetStartingFolders(string path, int count) {
199215
return result;
200216
}
201217

218+
public static IEnumerable<Type> GetValidTypes(this Assembly assembly) {
219+
Type[] types;
220+
try {
221+
types = assembly.GetTypes();
222+
} catch (ReflectionTypeLoadException e) {
223+
types = e.Types;
224+
}
225+
226+
return types.Where(t => t != null);
227+
}
228+
202229
#if UNITY_2020_3_OR_NEWER
203230
public static SerializedProperty? GetCustomRenderPipelineProperty() {
204231
#else
@@ -398,7 +425,11 @@ public static void ReimportFromExport() {
398425

399426
[MenuItem("Assets/Experimental/Re-import from Export", true)]
400427
public static bool ReimportFromExportValidate() {
428+
#if UNITY_2020_3_OR_NEWER
401429
if (Selection.count != 1) return false;
430+
#else
431+
if (Selection.objects.Length != 1) return false;
432+
#endif
402433

403434
var activeObject = Selection.activeObject;
404435
var assetPath = AssetDatabase.GetAssetPath(Selection.activeObject);
@@ -415,5 +446,63 @@ public static bool ReimportFromExportValidate() {
415446

416447
return true;
417448
}
449+
450+
public static void StartProfiler() {
451+
Profiler.logFile = "scrub_project_profile";
452+
Profiler.enableBinaryLog = true;
453+
Profiler.enabled = true;
454+
Profiler.enableAllocationCallstacks = true;
455+
// Profiler.maxUsedMemory = 512 * 1024 * 1024;
456+
}
457+
458+
public static void StopProfiler() {
459+
Profiler.enabled = false;
460+
Profiler.logFile = "";
461+
Profiler.enableBinaryLog = false;
462+
Profiler.enableAllocationCallstacks = false;
463+
}
464+
465+
public static Task ForEachAsync<TSource, TResult>(
466+
this IEnumerable<TSource> source,
467+
Func<TSource, Task<TResult>> taskSelector, Action<TSource, TResult> resultProcessor) {
468+
var oneAtATime = new SemaphoreSlim(initialCount: 1, maxCount: 1);
469+
return Task.WhenAll(
470+
from item in source
471+
select ProcessAsync(item, taskSelector, resultProcessor, oneAtATime));
472+
}
473+
474+
private static async Task ProcessAsync<TSource, TResult>(
475+
TSource item,
476+
Func<TSource, Task<TResult>> taskSelector, Action<TSource, TResult> resultProcessor,
477+
SemaphoreSlim oneAtATime) {
478+
TResult result = await taskSelector(item);
479+
await oneAtATime.WaitAsync();
480+
try { resultProcessor(item, result); }
481+
finally { oneAtATime.Release(); }
482+
}
483+
}
484+
}
485+
486+
#if !UNITY_2020_3_OR_NEWER
487+
public static class OldUnityExtensions {
488+
public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value) {
489+
if (dictionary.ContainsKey(key)) return false;
490+
dictionary.Add(key, value);
491+
return true;
492+
}
493+
494+
public static bool TryPop<T>(this Stack<T> stack, out T value) {
495+
if (stack.Count == 0) {
496+
value = default;
497+
return false;
498+
}
499+
500+
value = stack.Pop();
501+
return true;
502+
}
503+
504+
public static string[] Split(this string value, char separator, int count) {
505+
return value.Split(new char[] { separator }, count);
418506
}
419-
}
507+
}
508+
#endif

Editor/PatcherWindow.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,7 @@ private void OnGUI() {
8181
}
8282

8383
if (GUILayout.Button("Run Patcher")) {
84-
var runFunction = PatcherUtility.GetGameWrapperRunFunction(_gameWrapperType);
85-
if (runFunction is null) {
84+
if (PatcherUtility.GetGameWrapperGetStepsFunction(_gameWrapperType) is null) {
8685
EditorUtility.DisplayDialog("Error", $"The {_gameWrapperType.Name} does not have a Run function", "Ok");
8786
return;
8887
}
@@ -94,11 +93,18 @@ private void OnGUI() {
9493
}
9594

9695
EditorApplication.delayCall += () => {
97-
runFunction.Invoke(null, null);
96+
PatcherSteps.Run();
9897
};
9998

10099
return;
101100
}
101+
102+
if (GUILayout.Button("Print Steps to Log")) {
103+
var pipeline = PatcherSteps.GetPipeline();
104+
if (pipeline != null) {
105+
pipeline.PrintToLog();
106+
}
107+
}
102108

103109
if (_gameWrapperGuiFunction != null) {
104110
try {

Editor/Steps/AfterStepAttribute.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace Nomnom.UnityProjectPatcher.Editor.Steps {
4+
[AttributeUsage(AttributeTargets.Class)]
5+
public sealed class AfterStepAttribute: Attribute {
6+
public readonly Type Type;
7+
8+
public AfterStepAttribute(Type type) {
9+
Type = type;
10+
}
11+
}
12+
}

Editor/Steps/AfterStepAttribute.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
namespace Nomnom.UnityProjectPatcher.Editor.Steps {
4+
[AttributeUsage(AttributeTargets.Class)]
5+
public sealed class BeforeStepAttribute: Attribute {
6+
public readonly Type Type;
7+
8+
public BeforeStepAttribute(Type type) {
9+
Type = type;
10+
}
11+
}
12+
}

Editor/Steps/BeforeStepAttribute.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Editor/Steps/Processing/CopyAssetRipperExportToProjectStep.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public UniTask<StepResult> Run() {
3939
for (var i = 0; i < allowedEntries.Length; i++) {
4040
var asset = allowedEntries[i];
4141

42-
EditorUtility.DisplayProgressBar("Copying assets", $"Copying {asset.RelativePathToRoot}", i / (float)allowedEntries.Length);
42+
EditorUtility.DisplayProgressBar($"Copying assets [{i}/{allowedEntries.Length}]", $"Copying {asset.RelativePathToRoot}", i / (float)allowedEntries.Length);
4343

4444
try {
4545
var projectPath = AssetScrubber.GetProjectPathFromExportPath(projectGameAssetsPath, asset, settings, arSettings, false);
@@ -83,7 +83,7 @@ public void OnComplete(bool failed) { }
8383
for (int i = 0; i < arAssets.Entries.Length; i++) {
8484
var asset = arAssets.Entries[i];
8585

86-
EditorUtility.DisplayProgressBar("Getting allowed entries", $"Scrubbing {asset.RelativePathToRoot}", i / (float) arAssets.Entries.Length);
86+
EditorUtility.DisplayProgressBar($"Getting allowed entries [{i}/{ arAssets.Entries.Length}]", $"Scrubbing {asset.RelativePathToRoot}", i / (float) arAssets.Entries.Length);
8787

8888
if (filesToExclude.Any(x => x == asset.RelativePathToRoot)) continue;
8989
if (filesToExcludePrefix.Any(x => asset.RelativePathToRoot.StartsWith(x))) continue;

0 commit comments

Comments
 (0)