From 33320821caa28e6f7bfbd2ec8c302080e632ecd0 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 01:06:45 +0100 Subject: [PATCH 001/116] removed useless assembly exclusions --- UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs index a629fdf4b..e52a11a0e 100644 --- a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs +++ b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs @@ -13,14 +13,7 @@ public static class StaticCtorPatchTargetInfo "mscorlib", "Mono.*", "Mono", - "MonoMod.*", - "BepInEx.*", - "BepInEx", - "MonoMod.*", - "0Harmony", - "HarmonyXInterop", - "StructureMap", - "Newtonsoft.Json" + "MonoMod.*" }; public static string[] AssemblyIncludeRaw { get; } = From dcd83101e494407ad1cc5fb42cd1d2c1572e3d79 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 13:31:21 +0100 Subject: [PATCH 002/116] ignore patching cctor on enum types --- UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index 82f4ce1ae..76f9b9f4c 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -38,6 +38,9 @@ public override void Patch(ref AssemblyDefinition assembly) StaticLogger.Log.LogDebug($"Removing readonly from static fields in {type.FullName}"); RemoveReadOnly(type); + // ignore enums + if (type.IsEnum) continue; + StaticLogger.Log.LogDebug($"Patching static ctor of {type.FullName}"); var staticCtor = ILCodeUtils.FindOrAddCctor(assembly, type); PatchStaticCtor(assembly, staticCtor, type); From a6dea845abab8dc2bb779406f43ec97972c0374b Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 13:34:09 +0100 Subject: [PATCH 003/116] ignore enum on readonly field removal too --- UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index 76f9b9f4c..a0c1e26be 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -34,6 +34,9 @@ public override void Patch(ref AssemblyDefinition assembly) foreach (var type in assembly.Modules.SelectMany(module => module.GetAllTypes())) { + // ignore enums + if (type.IsEnum) continue; + // remove readonly from all static fields StaticLogger.Log.LogDebug($"Removing readonly from static fields in {type.FullName}"); RemoveReadOnly(type); From e26bab03b9e45d05b84b5e04166714b19aefd0a2 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 17:36:34 +0100 Subject: [PATCH 004/116] ignore cctor if type doesn't have one --- UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index a0c1e26be..f3c377d92 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -41,8 +41,9 @@ public override void Patch(ref AssemblyDefinition assembly) StaticLogger.Log.LogDebug($"Removing readonly from static fields in {type.FullName}"); RemoveReadOnly(type); - // ignore enums - if (type.IsEnum) continue; + // why track for cctor when there is none + var staticCtor = type.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); + if (staticCtor == null) continue; StaticLogger.Log.LogDebug($"Patching static ctor of {type.FullName}"); var staticCtor = ILCodeUtils.FindOrAddCctor(assembly, type); From 2bdd2613d6d929a5a44a6833a839aa0bcb1ea799 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 17:37:02 +0100 Subject: [PATCH 005/116] hide unused public method --- UniTAS/Patcher/Utils/ILCodeUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/ILCodeUtils.cs b/UniTAS/Patcher/Utils/ILCodeUtils.cs index 1db50456a..fed0e1ce4 100644 --- a/UniTAS/Patcher/Utils/ILCodeUtils.cs +++ b/UniTAS/Patcher/Utils/ILCodeUtils.cs @@ -37,7 +37,7 @@ public static void MethodInvokeHook(AssemblyDefinition assembly, MethodDefinitio $"Added invoke hook to method {method.Name} of {methodDefinition.DeclaringType.FullName} invoking {method.DeclaringType?.FullName ?? "unknown"}.{method.Name}"); } - public static MethodDefinition FindOrAddCctor(AssemblyDefinition assembly, TypeDefinition type) + private static MethodDefinition FindOrAddCctor(AssemblyDefinition assembly, TypeDefinition type) { var staticCtor = type.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); if (staticCtor != null) return staticCtor; From f04bc783b68ca64e6a27121321def6a4448dd09a Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 18:17:09 +0100 Subject: [PATCH 006/116] removed adding unused cctor --- UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index f3c377d92..e552111e6 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -46,7 +46,6 @@ public override void Patch(ref AssemblyDefinition assembly) if (staticCtor == null) continue; StaticLogger.Log.LogDebug($"Patching static ctor of {type.FullName}"); - var staticCtor = ILCodeUtils.FindOrAddCctor(assembly, type); PatchStaticCtor(assembly, staticCtor, type); } } From 35133ca3a9a79046b64fea62d81e00bcb815a376 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 20:42:31 +0100 Subject: [PATCH 007/116] created extension replace and insert instruction --- .../Extensions/ILProcessorExtensions.cs | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 UniTAS/Patcher/Extensions/ILProcessorExtensions.cs diff --git a/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs b/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs new file mode 100644 index 000000000..460432c76 --- /dev/null +++ b/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs @@ -0,0 +1,81 @@ +using Mono.Cecil.Cil; + +namespace UniTAS.Patcher.Extensions; + +public static class ILProcessorExtensions +{ + /// + /// Inserts an instruction before the specified instruction as if it were replacing it.

+ /// This also fixes:
+ /// - Jump targets of any instructions that jump to the replaced instruction.
+ /// - Exception range fixing where if the replaced instruction is in a try block, the new instruction will be added to the try block and vice versa.
+ ///
+ /// + /// + /// + public static void InsertBeforeInstructionReplace(this ILProcessor ilProcessor, Instruction replacingInstruction, + Instruction newInstruction) + { + if (replacingInstruction == null || newInstruction == null) return; + + // insert + ilProcessor.InsertBefore(replacingInstruction, newInstruction); + + var instructions = ilProcessor.Body.Instructions; + + // fix jump targets + foreach (var instruction in instructions) + { + if (instruction.Operand == replacingInstruction) + { + instruction.Operand = newInstruction; + continue; + } + + // switch + if (instruction.Operand is not Instruction[] instructionsArray) continue; + + for (var i = 0; i < instructionsArray.Length; i++) + { + if (instructionsArray[i] == replacingInstruction) + { + instructionsArray[i] = newInstruction; + } + } + } + + // fix exception ranges + if (!ilProcessor.Body.HasExceptionHandlers) return; + + foreach (var exceptionHandler in ilProcessor.Body.ExceptionHandlers) + { + // try + if (exceptionHandler.TryStart == replacingInstruction) + { + exceptionHandler.TryStart = newInstruction; + } + + if (exceptionHandler.TryEnd == replacingInstruction) + { + exceptionHandler.TryEnd = newInstruction; + } + + // catch / finally + if (exceptionHandler.HandlerStart == replacingInstruction) + { + exceptionHandler.HandlerStart = newInstruction; + } + + if (exceptionHandler.HandlerEnd == replacingInstruction) + { + exceptionHandler.HandlerEnd = newInstruction; + } + + // some special case idk + if (exceptionHandler.FilterStart == replacingInstruction) + { + exceptionHandler.FilterStart = newInstruction; + } + } + } +} \ No newline at end of file From d1c992f63b1801ee5f4c518efb9ec47b638a7114 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 21:57:14 +0100 Subject: [PATCH 008/116] null check and warns for safety --- UniTAS/Patcher/Extensions/ILProcessorExtensions.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs b/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs index 460432c76..5c38aaa21 100644 --- a/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs +++ b/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs @@ -1,4 +1,5 @@ using Mono.Cecil.Cil; +using UniTAS.Patcher.Utils; namespace UniTAS.Patcher.Extensions; @@ -16,7 +17,17 @@ public static class ILProcessorExtensions public static void InsertBeforeInstructionReplace(this ILProcessor ilProcessor, Instruction replacingInstruction, Instruction newInstruction) { - if (replacingInstruction == null || newInstruction == null) return; + if (ilProcessor.Body == null) + { + StaticLogger.Log.LogWarning("Body is null, skipping"); + return; + } + + if (replacingInstruction == null || newInstruction == null) + { + StaticLogger.Log.LogWarning("Instruction is null, skipping"); + return; + } // insert ilProcessor.InsertBefore(replacingInstruction, newInstruction); From a19b000cce20e9db3e3dd784e0544913378e702e Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 23:36:40 +0100 Subject: [PATCH 009/116] guarantee event method invoke --- .../Patcher/Patches/Preloader/MonoBehaviourPatch.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs index 7b82b14f0..c84fb4fef 100644 --- a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs @@ -245,12 +245,6 @@ public override void Patch(ref AssemblyDefinition assembly) foundMethod.Body.OptimizeMacros(); } - // event methods invoke - foreach (var eventMethodPair in EventMethods) - { - InvokeUnityEventMethod(type, eventMethodPair.Item1, assembly, eventMethodPair.Item2); - } - // update skip check and related methods var updateMethods = new[] { @@ -264,6 +258,12 @@ public override void Patch(ref AssemblyDefinition assembly) } StaticLogger.Log.LogDebug("Patched Update related methods for skipping execution"); + + // event methods invoke + foreach (var eventMethodPair in EventMethods) + { + InvokeUnityEventMethod(type, eventMethodPair.Item1, assembly, eventMethodPair.Item2); + } } } From c8b5378cddc0ad5d80732470727a928806048718 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 23:37:19 +0100 Subject: [PATCH 010/116] updated --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e64c0078..cca1e1d8c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed accidentally skipping over static constructors - Fixed accidentally skipping over removing some readonly fields - Fixed crash when trying to remove readonly fields +- Fixed sometimes UniTAS not receiving the first update, which might cause some movie desync by 1 frame ## Changed From d553c440f9ad8db64f5db8756c001d874c471f08 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 23:40:16 +0100 Subject: [PATCH 011/116] added fixing to be optional by flag --- .../Extensions/ILProcessorExtensions.cs | 44 ++++++++++++------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs b/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs index 5c38aaa21..8d2f5199f 100644 --- a/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs +++ b/UniTAS/Patcher/Extensions/ILProcessorExtensions.cs @@ -1,3 +1,4 @@ +using System; using Mono.Cecil.Cil; using UniTAS.Patcher.Utils; @@ -11,11 +12,8 @@ public static class ILProcessorExtensions /// - Jump targets of any instructions that jump to the replaced instruction.
/// - Exception range fixing where if the replaced instruction is in a try block, the new instruction will be added to the try block and vice versa.
/// - /// - /// - /// public static void InsertBeforeInstructionReplace(this ILProcessor ilProcessor, Instruction replacingInstruction, - Instruction newInstruction) + Instruction newInstruction, InstructionReplaceFixType fixType = InstructionReplaceFixType.All) { if (ilProcessor.Body == null) { @@ -35,28 +33,34 @@ public static void InsertBeforeInstructionReplace(this ILProcessor ilProcessor, var instructions = ilProcessor.Body.Instructions; // fix jump targets - foreach (var instruction in instructions) + if ((fixType & InstructionReplaceFixType.JumpTargets) != 0) { - if (instruction.Operand == replacingInstruction) + foreach (var instruction in instructions) { - instruction.Operand = newInstruction; - continue; - } + if (instruction == newInstruction) continue; - // switch - if (instruction.Operand is not Instruction[] instructionsArray) continue; + if (instruction.Operand == replacingInstruction) + { + instruction.Operand = newInstruction; + continue; + } - for (var i = 0; i < instructionsArray.Length; i++) - { - if (instructionsArray[i] == replacingInstruction) + // switch + if (instruction.Operand is not Instruction[] instructionsArray) continue; + + for (var i = 0; i < instructionsArray.Length; i++) { - instructionsArray[i] = newInstruction; + if (instructionsArray[i] == replacingInstruction) + { + instructionsArray[i] = newInstruction; + } } } } // fix exception ranges - if (!ilProcessor.Body.HasExceptionHandlers) return; + if ((fixType & InstructionReplaceFixType.ExceptionRanges) == 0 || + !ilProcessor.Body.HasExceptionHandlers) return; foreach (var exceptionHandler in ilProcessor.Body.ExceptionHandlers) { @@ -89,4 +93,12 @@ public static void InsertBeforeInstructionReplace(this ILProcessor ilProcessor, } } } +} + +[Flags] +public enum InstructionReplaceFixType +{ + JumpTargets = 1, + ExceptionRanges = 2, + All = JumpTargets | ExceptionRanges } \ No newline at end of file From 1427c919b4d2b4442c7b430dd665bc0c62a2c519 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 23:40:44 +0100 Subject: [PATCH 012/116] comment to make sure i dont flop it up --- UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs index ea4ce0da2..35f70dbdd 100644 --- a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs @@ -44,6 +44,8 @@ public override void Patch(ref AssemblyDefinition assembly) var disableFinalizeInvokeReference = assembly.MainModule.ImportReference(disableFinalizeInvoke); // basically, if DisableFinalizeInvoke is true, return + + // also this doesn't need the try-catch block ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, disableFinalizeInvokeReference)); ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Brfalse, firstInstruction)); From 49b742381f391a96bc2ed2484ba8b033cd96f0c6 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 23:46:18 +0100 Subject: [PATCH 013/116] fixed inserting instructions --- .../Patches/Preloader/MonoBehaviourPatch.cs | 39 ++++++++++------ .../Patches/Preloader/StaticCtorHeaders.cs | 25 +++++----- UniTAS/Patcher/Utils/ILCodeUtils.cs | 46 ++++++------------- 3 files changed, 52 insertions(+), 58 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs index c84fb4fef..4abaec4cb 100644 --- a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs @@ -219,8 +219,10 @@ public override void Patch(ref AssemblyDefinition assembly) var firstInstruction = il.Body.Instructions.First(); // return early check - il.InsertBefore(firstInstruction, il.Create(OpCodes.Call, pauseExecutionReference)); - il.InsertBefore(firstInstruction, il.Create(OpCodes.Brfalse_S, firstInstruction)); + il.InsertBeforeInstructionReplace(firstInstruction, il.Create(OpCodes.Call, pauseExecutionReference), + InstructionReplaceFixType.ExceptionRanges); + il.InsertBeforeInstructionReplace(firstInstruction, il.Create(OpCodes.Brfalse_S, firstInstruction), + InstructionReplaceFixType.ExceptionRanges); // if the return type isn't void, we need to return a default value if (foundMethod.ReturnType != assembly.MainModule.TypeSystem.Void) @@ -230,17 +232,23 @@ public override void Patch(ref AssemblyDefinition assembly) { var local = new VariableDefinition(foundMethod.ReturnType); il.Body.Variables.Add(local); - il.InsertBefore(firstInstruction, il.Create(OpCodes.Ldloca_S, local)); - il.InsertBefore(firstInstruction, il.Create(OpCodes.Initobj, foundMethod.ReturnType)); - il.InsertBefore(firstInstruction, il.Create(OpCodes.Ldloc_S, local)); + il.InsertBeforeInstructionReplace(firstInstruction, il.Create(OpCodes.Ldloca_S, local), + InstructionReplaceFixType.ExceptionRanges); + il.InsertBeforeInstructionReplace(firstInstruction, + il.Create(OpCodes.Initobj, foundMethod.ReturnType), + InstructionReplaceFixType.ExceptionRanges); + il.InsertBeforeInstructionReplace(firstInstruction, il.Create(OpCodes.Ldloc_S, local), + InstructionReplaceFixType.ExceptionRanges); } else { - il.InsertBefore(firstInstruction, il.Create(OpCodes.Ldnull)); + il.InsertBeforeInstructionReplace(firstInstruction, il.Create(OpCodes.Ldnull), + InstructionReplaceFixType.ExceptionRanges); } } - il.InsertBefore(firstInstruction, il.Create(OpCodes.Ret)); + il.InsertBeforeInstructionReplace(firstInstruction, il.Create(OpCodes.Ret), + InstructionReplaceFixType.ExceptionRanges); foundMethod.Body.OptimizeMacros(); } @@ -276,17 +284,20 @@ private static void UpdateEarlyReturn(MethodDefinition skipCheckMethod, Assembly var updateFirstInstruction = updateIl.Body.Instructions.First(); // return early check - updateIl.InsertBefore(updateFirstInstruction, updateIl.Create(OpCodes.Call, pausedUpdateReference)); - updateIl.InsertBefore(updateFirstInstruction, - updateIl.Create(OpCodes.Brfalse_S, updateFirstInstruction)); + updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, + updateIl.Create(OpCodes.Call, pausedUpdateReference), InstructionReplaceFixType.ExceptionRanges); + updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, + updateIl.Create(OpCodes.Brfalse_S, updateFirstInstruction), InstructionReplaceFixType.ExceptionRanges); // if the return type isn't void, we need to return a default value if (skipCheckMethod.ReturnType != assembly.MainModule.TypeSystem.Void) { - updateIl.InsertBefore(updateFirstInstruction, updateIl.Create(OpCodes.Ldnull)); + updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, updateIl.Create(OpCodes.Ldnull), + InstructionReplaceFixType.ExceptionRanges); } - updateIl.InsertBefore(updateFirstInstruction, updateIl.Create(OpCodes.Ret)); + updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, updateIl.Create(OpCodes.Ret), + InstructionReplaceFixType.ExceptionRanges); skipCheckMethod.Body.OptimizeMacros(); } @@ -301,7 +312,9 @@ private static void InvokeUnityEventMethod(TypeDefinition type, string methodNam var ilProcessor = method.Body.GetILProcessor(); var reference = assembly.MainModule.ImportReference(eventInvoker); - ilProcessor.InsertBefore(method.Body.Instructions.First(), ilProcessor.Create(OpCodes.Call, reference)); + // shouldn't be referenced by anything as it gets invoked once per type + ilProcessor.InsertBeforeInstructionReplace(method.Body.Instructions.First(), + ilProcessor.Create(OpCodes.Call, reference), InstructionReplaceFixType.ExceptionRanges); method.Body.OptimizeMacros(); diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index e552111e6..3b5176a11 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -90,7 +90,8 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio // insert call to our method at the start of the static ctor var startRefInstruction = ilProcessor.Create(OpCodes.Call, patchMethodStartRef); - ilProcessor.InsertBefore(first, startRefInstruction); + ilProcessor.InsertBeforeInstructionReplace(first, startRefInstruction, + InstructionReplaceFixType.ExceptionRanges); insertedInstructions.Add(startRefInstruction); StaticLogger.Log.LogDebug($"Patched start of static ctor of {type.FullName}"); @@ -98,28 +99,27 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio for (var i = 0; i < instructions.Count; i++) { - var instruction = instructions[i]; - if (instruction.OpCode != OpCodes.Ret) continue; + var retInstruction = instructions[i]; + if (retInstruction.OpCode != OpCodes.Ret) continue; + var endRefInstruction = ilProcessor.Create(OpCodes.Call, patchMethodEndRef); - ilProcessor.InsertBefore(instruction, endRefInstruction); + ilProcessor.InsertBeforeInstructionReplace(retInstruction, endRefInstruction); insertedInstructions.Add(endRefInstruction); + StaticLogger.Log.LogDebug( - $"Found return in static ctor of {type.FullName}, instruction: {instruction}, patched"); - ILCodeUtils.RedirectJumpsToNewDest(ilProcessor.Body.Instructions, instruction, endRefInstruction); + $"Found return in static ctor of {type.FullName}, instruction: {retInstruction}, patched"); + i++; insertCount++; } if (insertCount == 0) { - var last = instructions.Last(); var endRefInstruction = ilProcessor.Create(OpCodes.Call, patchMethodEndRef); - ilProcessor.InsertAfter(last, endRefInstruction); + ilProcessor.InsertBeforeInstructionReplace(instructions.Last(), endRefInstruction); insertedInstructions.Add(endRefInstruction); StaticLogger.Log.LogDebug( $"Found no returns in static ctor of {type.FullName}, force patching at last instruction"); - - ILCodeUtils.RedirectJumpsToNewDest(ilProcessor.Body.Instructions, last, endRefInstruction); } // i gotta find a nice place to insert call to CheckAndInvokeDependency @@ -146,15 +146,12 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio // ok we found it, insert call to CheckAndInvokeDependency StaticLogger.Log.LogDebug("Before insert"); var dependencyRefInstruction = ilProcessor.Create(OpCodes.Call, patchMethodDependencyRef); - ilProcessor.InsertBefore(instruction, dependencyRefInstruction); + ilProcessor.InsertBeforeInstructionReplace(instruction, dependencyRefInstruction); insertedDependencyInvoke = true; StaticLogger.Log.LogDebug( $"Found external class reference in static ctor of {type.FullName}, instruction: {instruction}, patched"); - ILCodeUtils.RedirectJumpsToNewDest(ilProcessor.Body.Instructions, instruction, - dependencyRefInstruction); - break; } diff --git a/UniTAS/Patcher/Utils/ILCodeUtils.cs b/UniTAS/Patcher/Utils/ILCodeUtils.cs index fed0e1ce4..cd860a7f2 100644 --- a/UniTAS/Patcher/Utils/ILCodeUtils.cs +++ b/UniTAS/Patcher/Utils/ILCodeUtils.cs @@ -3,7 +3,7 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; -using Mono.Collections.Generic; +using UniTAS.Patcher.Extensions; using MethodAttributes = Mono.Cecil.MethodAttributes; namespace UniTAS.Patcher.Utils; @@ -25,13 +25,22 @@ public static void MethodInvokeHook(AssemblyDefinition assembly, MethodDefinitio var invoke = assembly.MainModule.ImportReference(method); - methodDefinition.Body.SimplifyMacros(); - var firstInstruction = methodDefinition.Body.Instructions.First(); - var ilProcessor = methodDefinition.Body.GetILProcessor(); + var body = methodDefinition.Body; + body.SimplifyMacros(); + var ilProcessor = body.GetILProcessor(); // insert call before first instruction - ilProcessor.InsertBefore(firstInstruction, ilProcessor.Create(OpCodes.Call, invoke)); - methodDefinition.Body.OptimizeMacros(); + if (body.Instructions.Count == 0) + { + ilProcessor.Append(ilProcessor.Create(OpCodes.Call, invoke)); + } + else + { + ilProcessor.InsertBeforeInstructionReplace(body.Instructions.First(), + ilProcessor.Create(OpCodes.Call, invoke), InstructionReplaceFixType.ExceptionRanges); + } + + body.OptimizeMacros(); StaticLogger.Log.LogDebug( $"Added invoke hook to method {method.Name} of {methodDefinition.DeclaringType.FullName} invoking {method.DeclaringType?.FullName ?? "unknown"}.{method.Name}"); @@ -53,29 +62,4 @@ private static MethodDefinition FindOrAddCctor(AssemblyDefinition assembly, Type il.Append(il.Create(OpCodes.Ret)); return staticCtor; } - - public static void RedirectJumpsToNewDest(Collection instructions, Instruction oldDest, - Instruction newDest) - { - foreach (var instruction in instructions) - { - if (instruction.Operand == oldDest) - { - instruction.Operand = newDest; - continue; - } - - // switch - if (instruction.Operand is Instruction[] instructionsArray) - { - for (var i = 0; i < instructionsArray.Length; i++) - { - if (instructionsArray[i] == oldDest) - { - instructionsArray[i] = newDest; - } - } - } - } - } } \ No newline at end of file From b0ca7dd3b4943fba8e27af7d2755335e9ef4a7bd Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 15 Sep 2023 23:58:39 +0100 Subject: [PATCH 014/116] return default value if return type isn't void --- .../Patches/Preloader/MonoBehaviourPatch.cs | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs index 4abaec4cb..acedf4830 100644 --- a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs @@ -290,10 +290,28 @@ private static void UpdateEarlyReturn(MethodDefinition skipCheckMethod, Assembly updateIl.Create(OpCodes.Brfalse_S, updateFirstInstruction), InstructionReplaceFixType.ExceptionRanges); // if the return type isn't void, we need to return a default value - if (skipCheckMethod.ReturnType != assembly.MainModule.TypeSystem.Void) + var skipCheckMethodReturn = skipCheckMethod.ReturnType; + if (skipCheckMethodReturn != assembly.MainModule.TypeSystem.Void) { - updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, updateIl.Create(OpCodes.Ldnull), - InstructionReplaceFixType.ExceptionRanges); + // if value type, we need to return a default value + if (skipCheckMethodReturn.IsValueType) + { + var local = new VariableDefinition(skipCheckMethodReturn); + updateIl.Body.Variables.Add(local); + updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, + updateIl.Create(OpCodes.Ldloca_S, local), + InstructionReplaceFixType.ExceptionRanges); + updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, + updateIl.Create(OpCodes.Initobj, skipCheckMethodReturn), + InstructionReplaceFixType.ExceptionRanges); + updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, updateIl.Create(OpCodes.Ldloc_S, local), + InstructionReplaceFixType.ExceptionRanges); + } + else + { + updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, updateIl.Create(OpCodes.Ldnull), + InstructionReplaceFixType.ExceptionRanges); + } } updateIl.InsertBeforeInstructionReplace(updateFirstInstruction, updateIl.Create(OpCodes.Ret), From 49c4d4cd86107ffa2f7a8092822354a7746329ac Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 01:19:28 +0100 Subject: [PATCH 015/116] cleaner syntax --- .../UnityFix/ClearLoadedAssetBundlesOnRestart.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/ClearLoadedAssetBundlesOnRestart.cs b/UniTAS/Patcher/Implementations/UnityFix/ClearLoadedAssetBundlesOnRestart.cs index dd79b37b8..f1f0439ea 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/ClearLoadedAssetBundlesOnRestart.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/ClearLoadedAssetBundlesOnRestart.cs @@ -17,7 +17,6 @@ public void NewInstance(AssetBundle assetBundle) _trackedAssetBundles.Add(assetBundle); } - public void NewInstance(AssetBundleCreateRequest assetBundleCreateRequest) { _trackedAssetBundleCreateRequests.Add(assetBundleCreateRequest); @@ -33,7 +32,7 @@ public void OnPreGameRestart() foreach (var assetBundleCreateRequest in _trackedAssetBundleCreateRequests) { - if (assetBundleCreateRequest == null || assetBundleCreateRequest.assetBundle == null) continue; + if (assetBundleCreateRequest?.assetBundle == null) continue; assetBundleCreateRequest.assetBundle.Unload(true); } From 69c37a7ab92326167562075c3d04e44d054bfb76 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 02:01:52 +0100 Subject: [PATCH 016/116] include Unity.ResourceManager --- UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs index e52a11a0e..041f195c8 100644 --- a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs +++ b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs @@ -19,6 +19,8 @@ public static class StaticCtorPatchTargetInfo public static string[] AssemblyIncludeRaw { get; } = { "Unity.InputSystem", - "UnityEngine.InputModule" + "UnityEngine.InputModule", + // high level API that manages assets + "Unity.ResourceManager" }; } \ No newline at end of file From bf66774d568f3084974de1fc37fc983946f7ceff Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 02:02:09 +0100 Subject: [PATCH 017/116] changed to target the same assemblies --- .../Patches/Preloader/MonoBehaviourPatch.cs | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs index acedf4830..148a4fb43 100644 --- a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs @@ -17,39 +17,12 @@ namespace UniTAS.Patcher.Patches.Preloader; public class MonoBehaviourPatch : PreloadPatcher { - private readonly string[] _assemblyExclusionsRaw = - { - "UnityEngine.*", - "UnityEngine", - "Unity.*", - "System.*", - "System", - "netstandard", - "mscorlib", - "Mono.*", - "Mono", - "MonoMod.*", - "BepInEx.*", - "BepInEx", - "MonoMod.*", - "0Harmony", - "HarmonyXInterop", - "StructureMap", - "Newtonsoft.Json" - }; - - private readonly string[] _assemblyIncludeRaw = - { - "Unity.InputSystem", - "UnityEngine.InputModule" - }; - public override IEnumerable TargetDLLs => TargetPatcherDlls.AllDLLs.Where(x => { var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); return fileWithoutExtension == null || - _assemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || - !_assemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); + StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); }); private const string COLLISION = "UnityEngine.Collision"; From 2b42f8ced7faabe8c519ce1e5d8f9fdcb4a2680b Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 02:15:01 +0100 Subject: [PATCH 018/116] stupid code why did I remove this --- UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index 3b5176a11..11731aaf2 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -41,10 +41,8 @@ public override void Patch(ref AssemblyDefinition assembly) StaticLogger.Log.LogDebug($"Removing readonly from static fields in {type.FullName}"); RemoveReadOnly(type); - // why track for cctor when there is none - var staticCtor = type.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); - if (staticCtor == null) continue; - + // we need to add a static ctor as it will be responsible for tracking and resetting static fields + var staticCtor = ILCodeUtils.FindOrAddCctor(assembly, type); StaticLogger.Log.LogDebug($"Patching static ctor of {type.FullName}"); PatchStaticCtor(assembly, staticCtor, type); } From 49a3dc4361c208994a4db43107645146c90d3652 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 02:15:06 +0100 Subject: [PATCH 019/116] made public for use --- UniTAS/Patcher/Utils/ILCodeUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/ILCodeUtils.cs b/UniTAS/Patcher/Utils/ILCodeUtils.cs index cd860a7f2..67da2e1fa 100644 --- a/UniTAS/Patcher/Utils/ILCodeUtils.cs +++ b/UniTAS/Patcher/Utils/ILCodeUtils.cs @@ -46,7 +46,7 @@ public static void MethodInvokeHook(AssemblyDefinition assembly, MethodDefinitio $"Added invoke hook to method {method.Name} of {methodDefinition.DeclaringType.FullName} invoking {method.DeclaringType?.FullName ?? "unknown"}.{method.Name}"); } - private static MethodDefinition FindOrAddCctor(AssemblyDefinition assembly, TypeDefinition type) + public static MethodDefinition FindOrAddCctor(AssemblyDefinition assembly, TypeDefinition type) { var staticCtor = type.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); if (staticCtor != null) return staticCtor; From 3e5d87753ad38db37bc73cc20e8efc8ad38c6853 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 02:23:48 +0100 Subject: [PATCH 020/116] clear tracked objects on restart --- .../AsyncOperationTracker.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Services/UnityAsyncOperationTracker/AsyncOperationTracker.cs b/UniTAS/Patcher/Services/UnityAsyncOperationTracker/AsyncOperationTracker.cs index 3da2f1b3f..50a4219b4 100644 --- a/UniTAS/Patcher/Services/UnityAsyncOperationTracker/AsyncOperationTracker.cs +++ b/UniTAS/Patcher/Services/UnityAsyncOperationTracker/AsyncOperationTracker.cs @@ -4,6 +4,7 @@ using System.Reflection; using HarmonyLib; using UniTAS.Patcher.Interfaces.DependencyInjection; +using UniTAS.Patcher.Interfaces.Events.SoftRestart; using UniTAS.Patcher.Interfaces.Events.UnityEvents.RunEvenPaused; using UniTAS.Patcher.Models.UnitySafeWrappers.SceneManagement; using UniTAS.Patcher.Services.Logging; @@ -16,7 +17,7 @@ namespace UniTAS.Patcher.Services.UnityAsyncOperationTracker; // ReSharper disable once ClassNeverInstantiated.Global [Singleton] public class AsyncOperationTracker : ISceneLoadTracker, IAssetBundleCreateRequestTracker, IAssetBundleRequestTracker, - IOnLastUpdateUnconditional, IAsyncOperationIsInvokingOnComplete + IOnLastUpdateUnconditional, IAsyncOperationIsInvokingOnComplete, IOnPreGameRestart { private readonly List _asyncLoads = new(); private readonly List _asyncLoadStalls = new(); @@ -45,6 +46,14 @@ public AsyncOperationTracker(ISceneWrapper sceneWrapper, ILogger logger) _logger = logger; } + public void OnPreGameRestart() + { + _asyncLoads.Clear(); + _asyncLoadStalls.Clear(); + _assetBundleCreateRequests.Clear(); + _assetBundleRequests.Clear(); + } + public void OnLastUpdateUnconditional() { foreach (var scene in _asyncLoads) From 401104da949c83a23dd83b746606582be40ad26b Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 02:39:41 +0100 Subject: [PATCH 021/116] reduce log spam to trace log --- .../Patches/Preloader/MonoBehaviourPatch.cs | 6 ++--- .../Patches/Preloader/StaticCtorHeaders.cs | 27 ++++++++++--------- UniTAS/Patcher/Utils/ILCodeUtils.cs | 4 +-- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs index 148a4fb43..ee9055b17 100644 --- a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs @@ -185,7 +185,7 @@ public override void Patch(ref AssemblyDefinition assembly) if (foundMethod is not { HasBody: true }) continue; - StaticLogger.Log.LogDebug($"Patching method for pausing execution {foundMethod.FullName}"); + StaticLogger.Trace($"Patching method for pausing execution {foundMethod.FullName}"); foundMethod.Body.SimplifyMacros(); var il = foundMethod.Body.GetILProcessor(); @@ -238,7 +238,7 @@ public override void Patch(ref AssemblyDefinition assembly) UpdateEarlyReturn(updateMethod, assembly, pausedUpdateReference); } - StaticLogger.Log.LogDebug("Patched Update related methods for skipping execution"); + StaticLogger.Trace("Patched Update related methods for skipping execution"); // event methods invoke foreach (var eventMethodPair in EventMethods) @@ -309,7 +309,7 @@ private static void InvokeUnityEventMethod(TypeDefinition type, string methodNam method.Body.OptimizeMacros(); - StaticLogger.Log.LogDebug( + StaticLogger.Trace( $"Successfully patched {methodName} for type {type.FullName} for updates, invokes {eventInvoker.Name}"); } } \ No newline at end of file diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index 11731aaf2..37c925256 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -37,13 +37,15 @@ public override void Patch(ref AssemblyDefinition assembly) // ignore enums if (type.IsEnum) continue; + StaticLogger.Log.LogDebug($"Patching {type.FullName} for readonly fields and cctor"); + // remove readonly from all static fields - StaticLogger.Log.LogDebug($"Removing readonly from static fields in {type.FullName}"); + StaticLogger.Trace($"Removing readonly from static fields in {type.FullName}"); RemoveReadOnly(type); // we need to add a static ctor as it will be responsible for tracking and resetting static fields var staticCtor = ILCodeUtils.FindOrAddCctor(assembly, type); - StaticLogger.Log.LogDebug($"Patching static ctor of {type.FullName}"); + StaticLogger.Trace("Patching static ctor"); PatchStaticCtor(assembly, staticCtor, type); } } @@ -91,7 +93,7 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio ilProcessor.InsertBeforeInstructionReplace(first, startRefInstruction, InstructionReplaceFixType.ExceptionRanges); insertedInstructions.Add(startRefInstruction); - StaticLogger.Log.LogDebug($"Patched start of static ctor of {type.FullName}"); + StaticLogger.Trace($"Patched start of static ctor of {type.FullName}"); var insertCount = 0; @@ -104,7 +106,7 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio ilProcessor.InsertBeforeInstructionReplace(retInstruction, endRefInstruction); insertedInstructions.Add(endRefInstruction); - StaticLogger.Log.LogDebug( + StaticLogger.Trace( $"Found return in static ctor of {type.FullName}, instruction: {retInstruction}, patched"); i++; @@ -116,7 +118,7 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio var endRefInstruction = ilProcessor.Create(OpCodes.Call, patchMethodEndRef); ilProcessor.InsertBeforeInstructionReplace(instructions.Last(), endRefInstruction); insertedInstructions.Add(endRefInstruction); - StaticLogger.Log.LogDebug( + StaticLogger.Trace( $"Found no returns in static ctor of {type.FullName}, force patching at last instruction"); } @@ -142,12 +144,12 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio if (m.DeclaringType == null || m.DeclaringType.Equals(type)) continue; // ok we found it, insert call to CheckAndInvokeDependency - StaticLogger.Log.LogDebug("Before insert"); + StaticLogger.Trace("Before insert"); var dependencyRefInstruction = ilProcessor.Create(OpCodes.Call, patchMethodDependencyRef); ilProcessor.InsertBeforeInstructionReplace(instruction, dependencyRefInstruction); insertedDependencyInvoke = true; - StaticLogger.Log.LogDebug( + StaticLogger.Trace( $"Found external class reference in static ctor of {type.FullName}, instruction: {instruction}, patched"); break; @@ -160,7 +162,7 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio if (!insertedDependencyInvoke) { - StaticLogger.Log.LogDebug( + StaticLogger.Trace( $"Found no external class reference in static ctor of {type.FullName}, patching to invoke after start of static ctor"); ilProcessor.InsertAfter(startRefInstruction, ilProcessor.Create(OpCodes.Call, patchMethodDependencyRef)); @@ -186,14 +188,14 @@ public static void StaticCtorStart() if (type == null) { - throw new NullReferenceException("Could not find type of static ctor, something went horribly wrong"); + StaticLogger.Log.LogError("Could not find type of static ctor, something went horribly wrong"); + return; } CctorInvokeStack.Add(type); if (IsNotFirstInvoke(type)) return; - StaticLogger.Log.LogDebug( - $"First static ctor invoke for {type.FullName}, stack count: {CctorInvokeStack.Count}"); + StaticLogger.Trace($"First static ctor invoke for {type.FullName}, stack count: {CctorInvokeStack.Count}"); // first invoke zone @@ -218,8 +220,7 @@ public static void StaticCtorStart() PendingIgnoreAddingInvokeList.Add(type); - StaticLogger.Log.LogDebug( - $"Found static ctor dependency, parent: {parent.FullName}, child: {type.FullName}"); + StaticLogger.Trace($"Found static ctor dependency, parent: {parent.FullName}, child: {type.FullName}"); } } diff --git a/UniTAS/Patcher/Utils/ILCodeUtils.cs b/UniTAS/Patcher/Utils/ILCodeUtils.cs index 67da2e1fa..3ccf02f9f 100644 --- a/UniTAS/Patcher/Utils/ILCodeUtils.cs +++ b/UniTAS/Patcher/Utils/ILCodeUtils.cs @@ -42,7 +42,7 @@ public static void MethodInvokeHook(AssemblyDefinition assembly, MethodDefinitio body.OptimizeMacros(); - StaticLogger.Log.LogDebug( + StaticLogger.Trace( $"Added invoke hook to method {method.Name} of {methodDefinition.DeclaringType.FullName} invoking {method.DeclaringType?.FullName ?? "unknown"}.{method.Name}"); } @@ -51,7 +51,7 @@ public static MethodDefinition FindOrAddCctor(AssemblyDefinition assembly, TypeD var staticCtor = type.Methods.FirstOrDefault(m => m.IsConstructor && m.IsStatic); if (staticCtor != null) return staticCtor; - StaticLogger.Log.LogDebug($"Adding cctor to {type.FullName}"); + StaticLogger.Trace($"Adding cctor to {type.FullName}"); staticCtor = new(".cctor", MethodAttributes.Static | MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, From 52fc43c80445c187bd89adfad5c8eb38ce6fc7aa Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 02:40:00 +0100 Subject: [PATCH 022/116] include all target dlls I need --- .../Patches/Preloader/FinalizeSuppressionPatch.cs | 2 +- .../Patches/Preloader/MonoBehaviourPatch.cs | 2 +- .../Patches/Preloader/StaticCtorHeaders.cs | 2 +- UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs | 15 ++------------- 4 files changed, 5 insertions(+), 16 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs index 35f70dbdd..d24acae27 100644 --- a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs @@ -18,7 +18,7 @@ public class FinalizeSuppressionPatch : PreloadPatcher { var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); return fileWithoutExtension == null || - StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + // StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); }); diff --git a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs index ee9055b17..24f68dd6a 100644 --- a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs @@ -21,7 +21,7 @@ public class MonoBehaviourPatch : PreloadPatcher { var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); return fileWithoutExtension == null || - StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + // StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); }); diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index 37c925256..57dadcbf7 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -24,7 +24,7 @@ public class StaticCtorHeaders : PreloadPatcher { var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); return fileWithoutExtension == null || - StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + // StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); }); diff --git a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs index 041f195c8..03de19e9b 100644 --- a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs +++ b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs @@ -4,23 +4,12 @@ public static class StaticCtorPatchTargetInfo { public static string[] AssemblyExclusionsRaw { get; } = { - "UnityEngine.*", - "UnityEngine", - "Unity.*", + // c# related "System.*", "System", "netstandard", "mscorlib", "Mono.*", - "Mono", - "MonoMod.*" - }; - - public static string[] AssemblyIncludeRaw { get; } = - { - "Unity.InputSystem", - "UnityEngine.InputModule", - // high level API that manages assets - "Unity.ResourceManager" + "Mono" }; } \ No newline at end of file From 6bb840a571d5242c3af0d4900061f704df60ce31 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 02:45:30 +0100 Subject: [PATCH 023/116] removed broken code --- .../Implementations/GUI/IMGUIUseSystemFont.cs | 48 ------------------- 1 file changed, 48 deletions(-) delete mode 100644 UniTAS/Patcher/Implementations/GUI/IMGUIUseSystemFont.cs diff --git a/UniTAS/Patcher/Implementations/GUI/IMGUIUseSystemFont.cs b/UniTAS/Patcher/Implementations/GUI/IMGUIUseSystemFont.cs deleted file mode 100644 index c4cb827d8..000000000 --- a/UniTAS/Patcher/Implementations/GUI/IMGUIUseSystemFont.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Linq; -using UniTAS.Patcher.Interfaces.DependencyInjection; -using UniTAS.Patcher.Services.Logging; -using UniTAS.Patcher.Services.UnityEvents; -using UniTAS.Patcher.Utils; -using UnityEngine; - -namespace UniTAS.Patcher.Implementations.GUI; - -[Register] -[ForceInstantiate] -[ExcludeRegisterIfTesting] -public class IMGUIUseSystemFont -{ - private readonly IUpdateEvents _updateEvents; - private readonly Font _font; - private readonly ILogger _logger; - - private const string FALLBACK_FONT_NAME = "Liberation Sans"; - - public IMGUIUseSystemFont(ILogger logger, IUpdateEvents updateEvents) - { - _logger = logger; - _updateEvents = updateEvents; - var fonts = ResourcesUtils.FindObjectsOfTypeAll(); - if (fonts.Length == 0) - { - logger.LogWarning("no fallback font found for unity IMGUI"); - return; - } - - _font = fonts.FirstOrDefault(x => x.fontNames.Contains(FALLBACK_FONT_NAME)); - if (_font == null) - { - logger.LogError($"couldn't find unity fallback font {FALLBACK_FONT_NAME}"); - return; - } - - _updateEvents.OnGUIUnconditional += OnGUIUnconditional; - } - - private void OnGUIUnconditional() - { - _updateEvents.OnGUIUnconditional -= OnGUIUnconditional; - UnityEngine.GUI.skin.font = _font; - _logger.LogInfo($"Set unity IMGUI font to {FALLBACK_FONT_NAME}"); - } -} \ No newline at end of file From aaeca64148735c4574b8e2ca77e379d7105efb61 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 16 Sep 2023 17:27:46 +0100 Subject: [PATCH 024/116] added Instruction match to skip dependency check --- UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index 57dadcbf7..36c0e4131 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -137,7 +137,7 @@ private static void PatchStaticCtor(AssemblyDefinition assembly, MethodDefinitio // don't bother with mscorlib if (Equals(operand.GetType().Assembly, typeof(string).Assembly)) continue; - if (operand is VariableDefinition) continue; + if (operand is VariableDefinition or Instruction) continue; if (operand is MemberReference m) { From 1f01841f6a2614f2b92828fc4f22da291c19fb10 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 17 Sep 2023 02:09:44 +0100 Subject: [PATCH 025/116] removed useless public modifier --- .../Interfaces/InputSystemOverride/IInputOverrideDevice.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs b/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs index adcff7c3d..b70cc8207 100644 --- a/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs +++ b/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs @@ -8,10 +8,10 @@ public interface IInputOverrideDevice /// /// Update the state of the TAS device /// - public void Update(); + void Update(); /// /// Called when the device is to be added to the input system /// - public void DeviceAdded(); + void DeviceAdded(); } \ No newline at end of file From 53d3aa4cd306317477b2772e45b47b6aa4f43efd Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 17 Sep 2023 02:32:53 +0100 Subject: [PATCH 026/116] addition of logging --- .../Implementations/UnityRuntimeInitAttributeInvoker.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs b/UniTAS/Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs index 089e21dca..b01394127 100644 --- a/UniTAS/Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs +++ b/UniTAS/Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs @@ -7,6 +7,7 @@ using HarmonyLib; using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Services; +using UniTAS.Patcher.Services.Logging; using UniTAS.Patcher.Services.UnityEvents; namespace UniTAS.Patcher.Implementations; @@ -22,11 +23,12 @@ public class UnityRuntimeInitAttributeInvoker private readonly IGameRestart _gameRestart; private readonly IUpdateEvents _updateEvents; + private readonly ILogger _logger; private bool _invokedBeforeSceneLoad; private bool _invokedBeforeStart; - public UnityRuntimeInitAttributeInvoker(IGameRestart gameRestart, IUpdateEvents updateEvents) + public UnityRuntimeInitAttributeInvoker(IGameRestart gameRestart, IUpdateEvents updateEvents, ILogger logger) { var runtimeInitializeOnLoadMethodAttribute = AccessTools.TypeByName("UnityEngine.RuntimeInitializeOnLoadMethodAttribute"); @@ -35,6 +37,7 @@ public UnityRuntimeInitAttributeInvoker(IGameRestart gameRestart, IUpdateEvents _gameRestart = gameRestart; _updateEvents = updateEvents; + _logger = logger; _gameRestart.OnGameRestart += GameRestart; _gameRestart.OnGameRestart += (_, _) => { @@ -98,6 +101,8 @@ private void InvokeBeforeSceneLoad() if (_invokedBeforeSceneLoad) return; _invokedBeforeSceneLoad = true; + _logger.LogDebug($"Invoking BeforeSceneLoad methods, callback count: {_beforeSceneLoad.Length}"); + foreach (var method in _beforeSceneLoad) { method.Invoke(null, null); @@ -109,6 +114,8 @@ private void InvokeBeforeStart() if (_invokedBeforeStart) return; _invokedBeforeStart = true; + _logger.LogDebug($"Invoking BeforeStart methods, callback count: {_beforeStart.Length}"); + foreach (var method in _beforeStart) { method.Invoke(null, null); From 2086d656f0b281529066947f62fe10a3bc397a78 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 17 Sep 2023 02:35:35 +0100 Subject: [PATCH 027/116] additional logging --- .../Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UniTAS/Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs b/UniTAS/Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs index b01394127..6905ff07a 100644 --- a/UniTAS/Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs +++ b/UniTAS/Patcher/Implementations/UnityRuntimeInitAttributeInvoker.cs @@ -105,6 +105,7 @@ private void InvokeBeforeSceneLoad() foreach (var method in _beforeSceneLoad) { + _logger.LogDebug($"Invoking BeforeSceneLoad method {method.DeclaringType?.Name}.{method.Name}"); method.Invoke(null, null); } } @@ -118,6 +119,7 @@ private void InvokeBeforeStart() foreach (var method in _beforeStart) { + _logger.LogDebug($"Invoking BeforeStart method {method.DeclaringType?.Name}.{method.Name}"); method.Invoke(null, null); } } From 2a5b5d18ee495dbbff3f55b27fe9fbed0442750c Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Wed, 20 Sep 2023 21:53:14 +0100 Subject: [PATCH 028/116] cleaned input system handling to something better --- .../Utils/LegacyInputSystemUtilsTest.cs | 93 ++- .../VirtualEnv/KeyEqualityCheck.cs | 31 - .../Movie/Engine/Modules/Key.cs | 9 +- .../NewInputSystem/KeyboardDeviceOverride.cs | 4 +- .../InputState/KeyboardStateEnvController.cs | 32 +- .../KeyboardStateEnvLegacySystem.cs | 24 +- .../KeyboardStateEnvNewSystem.cs | 11 +- .../VirtualEnvironment/KeyFactory.cs | 219 ------- .../Patcher/Models/VirtualEnvironment/Key.cs | 49 -- .../Models/VirtualEnvironment/KeyCodeWrap.cs | 24 + .../VirtualEnvironment/NewKeyCodeWrap.cs | 24 + .../Patches/Harmony/LegacyInputPatch.cs | 28 +- .../RuntimeTests/LegacyInputSystemTests.cs | 16 +- .../VirtualEnvironment/IKeyFactory.cs | 10 - .../Input/IKeyboardStateEnvController.cs | 6 +- .../IKeyboardStateEnvLegacySystem.cs | 12 +- .../IKeyboardStateEnvNewSystem.cs | 7 +- UniTAS/Patcher/Utils/InputSystemUtils.cs | 534 ++++++++++++++++++ .../Patcher/Utils/LegacyInputSystemUtils.cs | 178 ------ 19 files changed, 709 insertions(+), 602 deletions(-) delete mode 100644 UniTAS/Patcher.Tests/VirtualEnv/KeyEqualityCheck.cs delete mode 100644 UniTAS/Patcher/Implementations/VirtualEnvironment/KeyFactory.cs delete mode 100644 UniTAS/Patcher/Models/VirtualEnvironment/Key.cs create mode 100644 UniTAS/Patcher/Models/VirtualEnvironment/KeyCodeWrap.cs create mode 100644 UniTAS/Patcher/Models/VirtualEnvironment/NewKeyCodeWrap.cs delete mode 100644 UniTAS/Patcher/Services/VirtualEnvironment/IKeyFactory.cs create mode 100644 UniTAS/Patcher/Utils/InputSystemUtils.cs delete mode 100644 UniTAS/Patcher/Utils/LegacyInputSystemUtils.cs diff --git a/UniTAS/Patcher.Tests/Utils/LegacyInputSystemUtilsTest.cs b/UniTAS/Patcher.Tests/Utils/LegacyInputSystemUtilsTest.cs index 98c558992..b3264cd89 100644 --- a/UniTAS/Patcher.Tests/Utils/LegacyInputSystemUtilsTest.cs +++ b/UniTAS/Patcher.Tests/Utils/LegacyInputSystemUtilsTest.cs @@ -8,134 +8,133 @@ public class LegacyInputSystemUtilsTest [Fact] public void Alphabet() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("a", out var keyCode); - Assert.True(ret); + var keyCode = InputSystemUtils.KeyCodeParse("a"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.A, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("z", out keyCode); - Assert.True(ret); + keyCode = InputSystemUtils.KeyCodeParse("z"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.Z, keyCode); } [Fact] public void AlphabetFail() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("aa", out var keyCode); - Assert.False(ret); + var keyCode = InputSystemUtils.KeyCodeParse("aa"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); } [Fact] public void Number() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("0", out var keyCode); - Assert.True(ret); + var keyCode = InputSystemUtils.KeyCodeParse("0"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.Alpha0, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("9", out keyCode); - Assert.True(ret); + keyCode = InputSystemUtils.KeyCodeParse("9"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.Alpha9, keyCode); } [Fact] public void NumberFail() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("10", out var keyCode); - Assert.False(ret); + var keyCode = InputSystemUtils.KeyCodeParse("10"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("-1", out keyCode); - Assert.False(ret); + keyCode = InputSystemUtils.KeyCodeParse("-1"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); } [Fact] public void Numpad() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("[0]", out var keyCode); - Assert.True(ret); + var keyCode = InputSystemUtils.KeyCodeParse("[0]"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.Keypad0, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("[9]", out keyCode); - Assert.True(ret); + keyCode = InputSystemUtils.KeyCodeParse("[9]"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.Keypad9, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("[+]", out keyCode); - Assert.True(ret); + keyCode = InputSystemUtils.KeyCodeParse("[+]"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.KeypadPlus, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("[-]", out keyCode); - Assert.True(ret); + keyCode = InputSystemUtils.KeyCodeParse("[-]"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.KeypadMinus, keyCode); } [Fact] public void NumpadFail() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("[-1]", out var keyCode); - Assert.False(ret); + var keyCode = InputSystemUtils.KeyCodeParse("[-1]"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("[10]", out keyCode); - Assert.False(ret); + keyCode = InputSystemUtils.KeyCodeParse("[10]"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("[+1]", out keyCode); - Assert.False(ret); + keyCode = InputSystemUtils.KeyCodeParse("[+1]"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); } [Fact] public void FunctionKey() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("f1", out var keyCode); - Assert.True(ret); + var keyCode = InputSystemUtils.KeyCodeParse("f1"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.F1, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("f15", out keyCode); - Assert.True(ret); + keyCode = InputSystemUtils.KeyCodeParse("f15"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.F15, keyCode); } [Fact] public void FunctionKeyFail() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("f0", out var keyCode); - Assert.False(ret); + var keyCode = InputSystemUtils.KeyCodeParse("f0"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); - ret = LegacyInputSystemUtils.KeyStringToKeyCode("f16", out keyCode); - Assert.False(ret); + keyCode = InputSystemUtils.KeyCodeParse("f16"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); } [Fact] public void MostOtherKey() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("backspace", out var keyCode); - Assert.True(ret); + var keyCode = InputSystemUtils.KeyCodeParse("backspace"); + Assert.NotNull(keyCode); Assert.Equal(KeyCode.Backspace, keyCode); } [Fact] public void OutOfRangeEnum() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("%", out var keyCode); - Assert.False(ret); + var keyCode = InputSystemUtils.KeyCodeParse("%"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); } [Fact] public void Empty() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("", out var keyCode); - Assert.False(ret); + var keyCode = InputSystemUtils.KeyCodeParse(""); + Assert.Null(keyCode); Assert.Equal(default, keyCode); } [Fact] public void Null() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode(null, out var keyCode); - Assert.False(ret); - Assert.Equal(default, keyCode); + var keyCode = InputSystemUtils.KeyCodeParse(null); + Assert.Null(keyCode); } [Fact] public void Invalid() { - var ret = LegacyInputSystemUtils.KeyStringToKeyCode("foo", out var keyCode); - Assert.False(ret); + var keyCode = InputSystemUtils.KeyCodeParse("foo"); + Assert.Null(keyCode); Assert.Equal(default, keyCode); } } \ No newline at end of file diff --git a/UniTAS/Patcher.Tests/VirtualEnv/KeyEqualityCheck.cs b/UniTAS/Patcher.Tests/VirtualEnv/KeyEqualityCheck.cs deleted file mode 100644 index 6fe7b0e5e..000000000 --- a/UniTAS/Patcher.Tests/VirtualEnv/KeyEqualityCheck.cs +++ /dev/null @@ -1,31 +0,0 @@ -using UniTAS.Patcher.Services.VirtualEnvironment; -using UnityEngine; - -namespace Patcher.Tests.VirtualEnv; - -public class KeyEqualityCheck -{ - [Fact] - public void KeyCodeEquals() - { - var container = KernelUtils.Init(); - var keyFactory = container.GetInstance(); - - var key1 = keyFactory.CreateKey(KeyCode.A); - var key2 = keyFactory.CreateKey(KeyCode.A); - - Assert.Equal(key1, key2); - } - - [Fact] - public void KeyCodeAndStringEquals() - { - var container = KernelUtils.Init(); - var keyFactory = container.GetInstance(); - - var key1 = keyFactory.CreateKey(KeyCode.A); - var key2 = keyFactory.CreateKey("A"); - - Assert.Equal(key1, key2); - } -} \ No newline at end of file diff --git a/UniTAS/Patcher/Implementations/Movie/Engine/Modules/Key.cs b/UniTAS/Patcher/Implementations/Movie/Engine/Modules/Key.cs index 48f7bd60b..1dc141edf 100644 --- a/UniTAS/Patcher/Implementations/Movie/Engine/Modules/Key.cs +++ b/UniTAS/Patcher/Implementations/Movie/Engine/Modules/Key.cs @@ -1,7 +1,6 @@ using System.Diagnostics.CodeAnalysis; using MoonSharp.Interpreter; using UniTAS.Patcher.Interfaces.Movie; -using UniTAS.Patcher.Services.VirtualEnvironment; using UniTAS.Patcher.Services.VirtualEnvironment.Input; namespace UniTAS.Patcher.Implementations.Movie.Engine.Modules; @@ -11,23 +10,21 @@ namespace UniTAS.Patcher.Implementations.Movie.Engine.Modules; public class Key : EngineMethodClass { private readonly IKeyboardStateEnvController _kbController; - private readonly IKeyFactory _keyFactory; [MoonSharpHidden] - public Key(IKeyboardStateEnvController kbController, IKeyFactory keyFactory) + public Key(IKeyboardStateEnvController kbController) { _kbController = kbController; - _keyFactory = keyFactory; } public void Hold(string key) { - _kbController.Hold(_keyFactory.CreateKey(key)); + _kbController.Hold(key); } public void Release(string key) { - _kbController.Release(_keyFactory.CreateKey(key)); + _kbController.Release(key); } public void Clear() diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs index b26c6e68d..f9314bb05 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs @@ -31,9 +31,7 @@ public void Update() var state = new KeyboardState(); foreach (var heldKey in _keyboardStateEnvNewSystem.HeldKeys) { - var heldKeyNewSystem = heldKey.NewInputSystemKey; - if (heldKeyNewSystem == null) continue; - state.Set(heldKeyNewSystem.Value, true); + state.Set(heldKey.Key, true); } InputSystem.QueueStateEvent(_keyboard, state); diff --git a/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/KeyboardStateEnvController.cs b/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/KeyboardStateEnvController.cs index ce42ab99b..973a806be 100644 --- a/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/KeyboardStateEnvController.cs +++ b/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/KeyboardStateEnvController.cs @@ -1,8 +1,8 @@ using UniTAS.Patcher.Interfaces.DependencyInjection; -using UniTAS.Patcher.Models.VirtualEnvironment; using UniTAS.Patcher.Services.VirtualEnvironment.Input; using UniTAS.Patcher.Services.VirtualEnvironment.Input.LegacyInputSystem; using UniTAS.Patcher.Services.VirtualEnvironment.Input.NewInputSystem; +using UniTAS.Patcher.Utils; namespace UniTAS.Patcher.Implementations.VirtualEnvironment.InputState; @@ -19,16 +19,34 @@ public KeyboardStateEnvController(IKeyboardStateEnvLegacySystem keyboardStateEnv _keyboardStateEnvNewSystem = keyboardStateEnvNewSystem; } - public void Hold(Key key) + public void Hold(string key) { - _keyboardStateEnvLegacySystem.Hold(key); - _keyboardStateEnvNewSystem.Hold(key); + InputSystemUtils.KeyStringToKeys(key, out var keyCode, out var newKey); + + if (keyCode.HasValue) + { + _keyboardStateEnvLegacySystem.Hold(keyCode.Value); + } + + if (newKey.HasValue) + { + _keyboardStateEnvNewSystem.Hold(newKey.Value); + } } - public void Release(Key key) + public void Release(string key) { - _keyboardStateEnvLegacySystem.Release(key); - _keyboardStateEnvNewSystem.Release(key); + InputSystemUtils.KeyStringToKeys(key, out var keyCode, out var newKey); + + if (keyCode.HasValue) + { + _keyboardStateEnvLegacySystem.Release(keyCode.Value); + } + + if (newKey.HasValue) + { + _keyboardStateEnvNewSystem.Release(newKey.Value); + } } public void Clear() diff --git a/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/LegacyInputSystem/KeyboardStateEnvLegacySystem.cs b/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/LegacyInputSystem/KeyboardStateEnvLegacySystem.cs index e9f1ca795..bef9151f9 100644 --- a/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/LegacyInputSystem/KeyboardStateEnvLegacySystem.cs +++ b/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/LegacyInputSystem/KeyboardStateEnvLegacySystem.cs @@ -2,20 +2,22 @@ using UniTAS.Patcher.Interfaces.VirtualEnvironment; using UniTAS.Patcher.Models.VirtualEnvironment; using UniTAS.Patcher.Services.VirtualEnvironment.Input.LegacyInputSystem; +using UnityEngine; namespace UniTAS.Patcher.Implementations.VirtualEnvironment.InputState.LegacyInputSystem; [Singleton] -public class KeyboardStateEnvLegacySystem : LegacyInputSystemButtonBasedDevice, IKeyboardStateEnvLegacySystem +public class KeyboardStateEnvLegacySystem : LegacyInputSystemButtonBasedDevice, + IKeyboardStateEnvLegacySystem { - public new void Hold(Key key) + public void Hold(KeyCode keyCodeWrap) { - base.Hold(key); + base.Hold(new(keyCodeWrap)); } - public new void Release(Key key) + public void Release(KeyCode keyCodeWrap) { - base.Release(key); + base.Release(new(keyCodeWrap)); } public void Clear() @@ -23,19 +25,19 @@ public void Clear() ReleaseAllButtons(); } - public bool IsKeyDown(Key key) + public bool IsKeyDown(KeyCode keyCodeWrap) { - return IsButtonDown(key); + return IsButtonDown(new(keyCodeWrap)); } - public bool IsKeyUp(Key key) + public bool IsKeyUp(KeyCode keyCodeWrap) { - return IsButtonUp(key); + return IsButtonUp(new(keyCodeWrap)); } - public bool IsKeyHeld(Key key) + public bool IsKeyHeld(KeyCode keyCodeWrap) { - return IsButtonHeld(key); + return IsButtonHeld(new(keyCodeWrap)); } public bool AnyKeyHeld => AnyButtonHeld; diff --git a/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/NewInputSystem/KeyboardStateEnvNewSystem.cs b/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/NewInputSystem/KeyboardStateEnvNewSystem.cs index f3da4ea98..a06f3f774 100644 --- a/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/NewInputSystem/KeyboardStateEnvNewSystem.cs +++ b/UniTAS/Patcher/Implementations/VirtualEnvironment/InputState/NewInputSystem/KeyboardStateEnvNewSystem.cs @@ -2,23 +2,26 @@ using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Models.VirtualEnvironment; using UniTAS.Patcher.Services.VirtualEnvironment.Input.NewInputSystem; +using UnityEngine.InputSystem; namespace UniTAS.Patcher.Implementations.VirtualEnvironment.InputState.NewInputSystem; [Singleton] public class KeyboardStateEnvNewSystem : Interfaces.VirtualEnvironment.InputState, IKeyboardStateEnvNewSystem { - public List HeldKeys { get; } = new(); + public List HeldKeys { get; } = new(); public void Hold(Key key) { - if (HeldKeys.Contains(key)) return; - HeldKeys.Add(key); + var keyCodeWrap = new NewKeyCodeWrap(key); + if (HeldKeys.Contains(keyCodeWrap)) return; + HeldKeys.Add(keyCodeWrap); } public void Release(Key key) { - HeldKeys.Remove(key); + var keyCodeWrap = new NewKeyCodeWrap(key); + HeldKeys.Remove(keyCodeWrap); } public void Clear() diff --git a/UniTAS/Patcher/Implementations/VirtualEnvironment/KeyFactory.cs b/UniTAS/Patcher/Implementations/VirtualEnvironment/KeyFactory.cs deleted file mode 100644 index f54db34a4..000000000 --- a/UniTAS/Patcher/Implementations/VirtualEnvironment/KeyFactory.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System; -using UniTAS.Patcher.Interfaces.DependencyInjection; -using UniTAS.Patcher.Models.VirtualEnvironment; -using UniTAS.Patcher.Services.InputSystemOverride; -using UniTAS.Patcher.Services.Logging; -using UniTAS.Patcher.Services.VirtualEnvironment; -using UnityEngine; - -namespace UniTAS.Patcher.Implementations.VirtualEnvironment; - -[Singleton] -public class KeyFactory : IKeyFactory -{ - private readonly INewInputSystemExists _newInputSystemExists; - private readonly ILogger _logger; - - public KeyFactory(INewInputSystemExists newInputSystemExists, ILogger logger) - { - _newInputSystemExists = newInputSystemExists; - _logger = logger; - } - - public Key CreateKey(string key) - { - var keyCode = ParseKeyCode(key); - return keyCode.HasValue ? CreateKey(keyCode.Value) : new(key, null); - } - - public Key CreateKey(KeyCode key) - { - return new(key, _newInputSystemExists.HasInputSystem ? NewKeyFromKeyCode(key) : null); - } - - private static KeyCode? ParseKeyCode(string key) - { - var keyClean = key.Trim().ToLower(); - try - { - return (KeyCode)Enum.Parse(typeof(KeyCode), keyClean, true); - } - catch (Exception) - { - return null; - } - } - - private UnityEngine.InputSystem.Key? NewKeyFromKeyCode(KeyCode keyCode) - { - switch (keyCode) - { - case KeyCode.None: - return UnityEngine.InputSystem.Key.None; - case KeyCode.Backspace: - return UnityEngine.InputSystem.Key.Backspace; - case KeyCode.Delete: - return UnityEngine.InputSystem.Key.Delete; - case KeyCode.Tab: - return UnityEngine.InputSystem.Key.Tab; - case KeyCode.Return: - return UnityEngine.InputSystem.Key.Enter; - case KeyCode.Pause: - return UnityEngine.InputSystem.Key.Pause; - case KeyCode.Escape: - return UnityEngine.InputSystem.Key.Escape; - case KeyCode.Space: - return UnityEngine.InputSystem.Key.Space; - case KeyCode.KeypadPeriod: - return UnityEngine.InputSystem.Key.NumpadPeriod; - case KeyCode.KeypadDivide: - return UnityEngine.InputSystem.Key.NumpadDivide; - case KeyCode.KeypadMultiply: - return UnityEngine.InputSystem.Key.NumpadMultiply; - case KeyCode.KeypadMinus: - return UnityEngine.InputSystem.Key.NumpadMinus; - case KeyCode.KeypadPlus: - return UnityEngine.InputSystem.Key.NumpadPlus; - case KeyCode.KeypadEnter: - return UnityEngine.InputSystem.Key.NumpadEnter; - case KeyCode.KeypadEquals: - return UnityEngine.InputSystem.Key.NumpadEquals; - case KeyCode.UpArrow: - return UnityEngine.InputSystem.Key.UpArrow; - case KeyCode.DownArrow: - return UnityEngine.InputSystem.Key.DownArrow; - case KeyCode.RightArrow: - return UnityEngine.InputSystem.Key.RightArrow; - case KeyCode.LeftArrow: - return UnityEngine.InputSystem.Key.LeftArrow; - case KeyCode.Insert: - return UnityEngine.InputSystem.Key.Insert; - case KeyCode.Home: - return UnityEngine.InputSystem.Key.Home; - case KeyCode.End: - return UnityEngine.InputSystem.Key.End; - case KeyCode.PageUp: - return UnityEngine.InputSystem.Key.PageUp; - case KeyCode.PageDown: - return UnityEngine.InputSystem.Key.PageDown; - case KeyCode.Quote: - return UnityEngine.InputSystem.Key.Quote; - case KeyCode.Comma: - return UnityEngine.InputSystem.Key.Comma; - case KeyCode.Minus: - return UnityEngine.InputSystem.Key.Minus; - case KeyCode.Period: - return UnityEngine.InputSystem.Key.Period; - case KeyCode.Slash: - return UnityEngine.InputSystem.Key.Slash; - case KeyCode.Semicolon: - return UnityEngine.InputSystem.Key.Semicolon; - case KeyCode.Equals: - return UnityEngine.InputSystem.Key.Equals; - case KeyCode.LeftBracket: - return UnityEngine.InputSystem.Key.LeftBracket; - case KeyCode.Backslash: - return UnityEngine.InputSystem.Key.Backslash; - case KeyCode.RightBracket: - return UnityEngine.InputSystem.Key.RightBracket; - case KeyCode.BackQuote: - return UnityEngine.InputSystem.Key.Backquote; - case KeyCode.Numlock: - return UnityEngine.InputSystem.Key.NumLock; - case KeyCode.CapsLock: - return UnityEngine.InputSystem.Key.CapsLock; - case KeyCode.ScrollLock: - return UnityEngine.InputSystem.Key.ScrollLock; - case KeyCode.RightShift: - return UnityEngine.InputSystem.Key.RightShift; - case KeyCode.LeftShift: - return UnityEngine.InputSystem.Key.LeftShift; - case KeyCode.RightControl: - return UnityEngine.InputSystem.Key.RightCtrl; - case KeyCode.LeftControl: - return UnityEngine.InputSystem.Key.LeftCtrl; - case KeyCode.RightAlt: - return UnityEngine.InputSystem.Key.RightAlt; - case KeyCode.LeftAlt: - return UnityEngine.InputSystem.Key.LeftAlt; - case KeyCode.LeftApple: - return UnityEngine.InputSystem.Key.LeftApple; - case KeyCode.LeftWindows: - return UnityEngine.InputSystem.Key.LeftWindows; - case KeyCode.RightApple: - return UnityEngine.InputSystem.Key.RightApple; - case KeyCode.RightWindows: - return UnityEngine.InputSystem.Key.RightWindows; - case KeyCode.AltGr: - return UnityEngine.InputSystem.Key.AltGr; - case KeyCode.Print: - return UnityEngine.InputSystem.Key.PrintScreen; - } - - var alphabetRange = GetFromRange(keyCode, KeyCode.A, KeyCode.Z, UnityEngine.InputSystem.Key.A, - UnityEngine.InputSystem.Key.Z); - if (alphabetRange != null) - { - return alphabetRange.Value; - } - - var f1Range = GetFromRange(keyCode, KeyCode.F1, KeyCode.F12, UnityEngine.InputSystem.Key.F1, - UnityEngine.InputSystem.Key.F12); - if (f1Range != null) - { - return f1Range.Value; - } - - var digitRange = GetFromRange(keyCode, KeyCode.Alpha0, KeyCode.Alpha9, UnityEngine.InputSystem.Key.Digit0, - UnityEngine.InputSystem.Key.Digit9); - - if (digitRange != null) - { - // -1 and wrap back if too low - var digitRangeValue = (int)digitRange.Value - 1; - const int digitLowestNew = (int)UnityEngine.InputSystem.Key.Digit1; - if (digitRangeValue < digitLowestNew) - { - digitRangeValue = (int)UnityEngine.InputSystem.Key.Digit9; - } - - return (UnityEngine.InputSystem.Key)digitRangeValue; - } - - var keypadRange = GetFromRange(keyCode, KeyCode.Keypad0, KeyCode.Keypad9, UnityEngine.InputSystem.Key.Numpad0, - UnityEngine.InputSystem.Key.Numpad9); - - if (keypadRange != null) - { - return keypadRange.Value; - } - - _logger.LogWarning($"Key code {keyCode} not found in legacy to new key code mapping"); - - return null; - } - - private static UnityEngine.InputSystem.Key? GetFromRange(KeyCode keyCode, KeyCode min, KeyCode max, - UnityEngine.InputSystem.Key minNew, UnityEngine.InputSystem.Key maxNew) - { - var keyCodeInt = (int)keyCode; - var minLegacyInt = (int)min; - var maxLegacyInt = (int)max; - var minNewInt = (int)minNew; - var maxNewInt = (int)maxNew; - - // check if out of range key code - if (keyCodeInt < minLegacyInt || keyCodeInt > maxLegacyInt) - { - return null; - } - - // check if out of range for new key codes - if (keyCodeInt - minLegacyInt + minNewInt > maxNewInt) - { - return null; - } - - return (UnityEngine.InputSystem.Key)(keyCodeInt - minLegacyInt + minNewInt); - } -} \ No newline at end of file diff --git a/UniTAS/Patcher/Models/VirtualEnvironment/Key.cs b/UniTAS/Patcher/Models/VirtualEnvironment/Key.cs deleted file mode 100644 index d86ee5ac7..000000000 --- a/UniTAS/Patcher/Models/VirtualEnvironment/Key.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using UnityEngine; - -namespace UniTAS.Patcher.Models.VirtualEnvironment; - -public readonly struct Key : IEquatable -{ - private string Keys { get; } = null; - private KeyCode? KeyCode { get; } = null; - public UnityEngine.InputSystem.Key? NewInputSystemKey { get; } - - public Key(string keys, UnityEngine.InputSystem.Key? newInputSystemKey) - { - Keys = keys; - NewInputSystemKey = newInputSystemKey; - } - - public Key(KeyCode keyCode, UnityEngine.InputSystem.Key? newInputSystemKey) - { - KeyCode = keyCode; - NewInputSystemKey = newInputSystemKey; - } - - public bool Equals(Key other) - { - return Keys == other.Keys && KeyCode == other.KeyCode && NewInputSystemKey == other.NewInputSystemKey; - } - - public override bool Equals(object obj) - { - return obj is Key other && Equals(other); - } - - public override int GetHashCode() - { - unchecked - { - var hashCode = (Keys != null ? Keys.GetHashCode() : 0); - hashCode = (hashCode * 397) ^ KeyCode.GetHashCode(); - hashCode = (hashCode * 397) ^ NewInputSystemKey.GetHashCode(); - return hashCode; - } - } - - public override string ToString() - { - return Keys ?? KeyCode.ToString(); - } -} \ No newline at end of file diff --git a/UniTAS/Patcher/Models/VirtualEnvironment/KeyCodeWrap.cs b/UniTAS/Patcher/Models/VirtualEnvironment/KeyCodeWrap.cs new file mode 100644 index 000000000..aee0523bf --- /dev/null +++ b/UniTAS/Patcher/Models/VirtualEnvironment/KeyCodeWrap.cs @@ -0,0 +1,24 @@ +using System; +using UnityEngine; + +namespace UniTAS.Patcher.Models.VirtualEnvironment; + +public readonly struct KeyCodeWrap : IEquatable +{ + public KeyCode KeyCode { get; } = KeyCode.None; + + public KeyCodeWrap(KeyCode keyCode) + { + KeyCode = keyCode; + } + + public bool Equals(KeyCodeWrap other) + { + return KeyCode == other.KeyCode; + } + + public override string ToString() + { + return KeyCode.ToString(); + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Models/VirtualEnvironment/NewKeyCodeWrap.cs b/UniTAS/Patcher/Models/VirtualEnvironment/NewKeyCodeWrap.cs new file mode 100644 index 000000000..68abaed9a --- /dev/null +++ b/UniTAS/Patcher/Models/VirtualEnvironment/NewKeyCodeWrap.cs @@ -0,0 +1,24 @@ +using System; +using UnityEngine.InputSystem; + +namespace UniTAS.Patcher.Models.VirtualEnvironment; + +public readonly struct NewKeyCodeWrap : IEquatable +{ + public Key Key { get; } + + public NewKeyCodeWrap(Key key) + { + Key = key; + } + + public bool Equals(NewKeyCodeWrap other) + { + return Key == other.Key; + } + + public override string ToString() + { + return Key.ToString(); + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Patches/Harmony/LegacyInputPatch.cs b/UniTAS/Patcher/Patches/Harmony/LegacyInputPatch.cs index 3dc7daed2..a41855799 100644 --- a/UniTAS/Patcher/Patches/Harmony/LegacyInputPatch.cs +++ b/UniTAS/Patcher/Patches/Harmony/LegacyInputPatch.cs @@ -42,9 +42,6 @@ public class LegacyInputPatch private static readonly IResetInputAxesState ResetInputAxesState = ContainerStarter.Kernel.GetInstance(); - private static readonly IKeyFactory KeyFactory = - ContainerStarter.Kernel.GetInstance(); - // gets called from GetKey [HarmonyPatch(typeof(Input), nameof(Input.GetKeyInt))] private class GetKeyInt @@ -60,7 +57,7 @@ private static bool Prefix(KeyCode key, ref bool __result) return true; if (!VirtualEnvController.RunVirtualEnvironment) return true; - __result = KeyboardStateEnvLegacySystem.IsKeyHeld(KeyFactory.CreateKey(key)); + __result = KeyboardStateEnvLegacySystem.IsKeyHeld(key); return false; } @@ -86,13 +83,13 @@ private static bool Prefix(string name, ref bool __result) if (!VirtualEnvController.RunVirtualEnvironment) return true; __result = false; - if (!LegacyInputSystemUtils.KeyStringToKeyCode(name, out var foundKeyCode)) + var keyCode = InputSystemUtils.KeyCodeParse(name); + if (keyCode == null) { - __result = KeyboardStateEnvLegacySystem.IsKeyHeld(KeyFactory.CreateKey(name)); return false; } - __result = KeyboardStateEnvLegacySystem.IsKeyHeld(KeyFactory.CreateKey(foundKeyCode)); + __result = KeyboardStateEnvLegacySystem.IsKeyHeld(keyCode.Value); return false; } @@ -117,13 +114,14 @@ private static bool Prefix(string name, ref bool __result) return true; if (!VirtualEnvController.RunVirtualEnvironment) return true; - if (!LegacyInputSystemUtils.KeyStringToKeyCode(name, out var foundKeyCode)) + __result = false; + var keyCode = InputSystemUtils.KeyCodeParse(name); + if (keyCode == null) { - __result = KeyboardStateEnvLegacySystem.IsKeyUp(KeyFactory.CreateKey(name)); return false; } - __result = KeyboardStateEnvLegacySystem.IsKeyUp(KeyFactory.CreateKey(foundKeyCode)); + __result = KeyboardStateEnvLegacySystem.IsKeyUp(keyCode.Value); return false; } @@ -148,7 +146,7 @@ private static bool Prefix(KeyCode key, ref bool __result) return true; if (!VirtualEnvController.RunVirtualEnvironment) return true; - __result = KeyboardStateEnvLegacySystem.IsKeyUp(KeyFactory.CreateKey(key)); + __result = KeyboardStateEnvLegacySystem.IsKeyUp(key); return false; } @@ -174,13 +172,13 @@ private static bool Prefix(string name, ref bool __result) if (!VirtualEnvController.RunVirtualEnvironment) return true; __result = false; - if (!LegacyInputSystemUtils.KeyStringToKeyCode(name, out var foundKeyCode)) + var keyCode = InputSystemUtils.KeyCodeParse(name); + if (keyCode == null) { - __result = KeyboardStateEnvLegacySystem.IsKeyDown(KeyFactory.CreateKey(name)); return false; } - __result = KeyboardStateEnvLegacySystem.IsKeyDown(KeyFactory.CreateKey(foundKeyCode)); + __result = KeyboardStateEnvLegacySystem.IsKeyDown(keyCode.Value); return false; } @@ -205,7 +203,7 @@ private static bool Prefix(KeyCode key, ref bool __result) return true; if (!VirtualEnvController.RunVirtualEnvironment) return true; - __result = KeyboardStateEnvLegacySystem.IsKeyDown(KeyFactory.CreateKey(key)); + __result = KeyboardStateEnvLegacySystem.IsKeyDown(key); return false; } diff --git a/UniTAS/Patcher/RuntimeTests/LegacyInputSystemTests.cs b/UniTAS/Patcher/RuntimeTests/LegacyInputSystemTests.cs index a14718c8c..4ff0aa853 100644 --- a/UniTAS/Patcher/RuntimeTests/LegacyInputSystemTests.cs +++ b/UniTAS/Patcher/RuntimeTests/LegacyInputSystemTests.cs @@ -19,17 +19,15 @@ public class LegacyInputSystemTests private readonly LegacyInputSystemDevice _mouseControllerBase; private readonly IVirtualEnvController _virtualEnvController; - private readonly IKeyFactory _keyFactory; public LegacyInputSystemTests(IKeyboardStateEnvLegacySystem keyboardController, - IVirtualEnvController virtualEnvController, IMouseStateEnvLegacySystem mouseController, IKeyFactory keyFactory) + IVirtualEnvController virtualEnvController, IMouseStateEnvLegacySystem mouseController) { _keyboardController = keyboardController; _keyboardControllerBase = (LegacyInputSystemDevice)keyboardController; _virtualEnvController = virtualEnvController; _mouseController = mouseController; _mouseControllerBase = (LegacyInputSystemDevice)mouseController; - _keyFactory = keyFactory; } [RuntimeTest] @@ -37,7 +35,7 @@ public void GetKeyTest() { _virtualEnvController.RunVirtualEnvironment = true; - _keyboardController.Hold(_keyFactory.CreateKey(KeyCode.A)); + _keyboardController.Hold(KeyCode.A); _keyboardControllerBase.MovieUpdate(false); RuntimeAssert.True(Input.GetKeyDown(KeyCode.A), "keycode down check"); @@ -57,7 +55,7 @@ public void GetKeyTest() RuntimeAssert.True(Input.GetKey(KeyCode.A), "keycode 3 check"); RuntimeAssert.True(Input.GetKey("a"), "string 3 check"); - _keyboardController.Release(_keyFactory.CreateKey(KeyCode.A)); + _keyboardController.Release(KeyCode.A); _keyboardControllerBase.MovieUpdate(false); RuntimeAssert.False(Input.GetKey(KeyCode.A), "keycode 4 check"); @@ -78,7 +76,7 @@ public void KeyDownTest() { _virtualEnvController.RunVirtualEnvironment = true; - _keyboardController.Hold(_keyFactory.CreateKey(KeyCode.A)); + _keyboardController.Hold(KeyCode.A); _keyboardControllerBase.MovieUpdate(false); RuntimeAssert.True(Input.GetKeyDown(KeyCode.A), "keycode down check 1"); @@ -95,7 +93,7 @@ public void KeyDownTest() RuntimeAssert.False(Input.GetKeyDown(KeyCode.A), "keycode down check 4"); - _keyboardController.Release(_keyFactory.CreateKey(KeyCode.A)); + _keyboardController.Release(KeyCode.A); _virtualEnvController.RunVirtualEnvironment = false; } @@ -104,7 +102,7 @@ public void KeyDownTest2() { _virtualEnvController.RunVirtualEnvironment = true; - _keyboardController.Hold(_keyFactory.CreateKey(KeyCode.A)); + _keyboardController.Hold(KeyCode.A); _keyboardControllerBase.MovieUpdate(false); RuntimeAssert.True(Input.GetKeyDown(KeyCode.A), "keycode down check 1"); @@ -113,7 +111,7 @@ public void KeyDownTest2() RuntimeAssert.False(Input.GetKeyDown(KeyCode.A), "keycode down check 2"); - _keyboardController.Release(_keyFactory.CreateKey(KeyCode.A)); + _keyboardController.Release(KeyCode.A); _virtualEnvController.RunVirtualEnvironment = false; } diff --git a/UniTAS/Patcher/Services/VirtualEnvironment/IKeyFactory.cs b/UniTAS/Patcher/Services/VirtualEnvironment/IKeyFactory.cs deleted file mode 100644 index 4312b8220..000000000 --- a/UniTAS/Patcher/Services/VirtualEnvironment/IKeyFactory.cs +++ /dev/null @@ -1,10 +0,0 @@ -using UniTAS.Patcher.Models.VirtualEnvironment; -using UnityEngine; - -namespace UniTAS.Patcher.Services.VirtualEnvironment; - -public interface IKeyFactory -{ - Key CreateKey(string key); - Key CreateKey(KeyCode key); -} \ No newline at end of file diff --git a/UniTAS/Patcher/Services/VirtualEnvironment/Input/IKeyboardStateEnvController.cs b/UniTAS/Patcher/Services/VirtualEnvironment/Input/IKeyboardStateEnvController.cs index a47c79bf0..8f1657b3f 100644 --- a/UniTAS/Patcher/Services/VirtualEnvironment/Input/IKeyboardStateEnvController.cs +++ b/UniTAS/Patcher/Services/VirtualEnvironment/Input/IKeyboardStateEnvController.cs @@ -1,5 +1,3 @@ -using UniTAS.Patcher.Models.VirtualEnvironment; - namespace UniTAS.Patcher.Services.VirtualEnvironment.Input; /// @@ -7,7 +5,7 @@ namespace UniTAS.Patcher.Services.VirtualEnvironment.Input; /// public interface IKeyboardStateEnvController { - void Hold(Key key); - void Release(Key key); + void Hold(string key); + void Release(string key); void Clear(); } \ No newline at end of file diff --git a/UniTAS/Patcher/Services/VirtualEnvironment/Input/LegacyInputSystem/IKeyboardStateEnvLegacySystem.cs b/UniTAS/Patcher/Services/VirtualEnvironment/Input/LegacyInputSystem/IKeyboardStateEnvLegacySystem.cs index 6cbd3af50..3e8289ad8 100644 --- a/UniTAS/Patcher/Services/VirtualEnvironment/Input/LegacyInputSystem/IKeyboardStateEnvLegacySystem.cs +++ b/UniTAS/Patcher/Services/VirtualEnvironment/Input/LegacyInputSystem/IKeyboardStateEnvLegacySystem.cs @@ -1,15 +1,15 @@ -using UniTAS.Patcher.Models.VirtualEnvironment; +using UnityEngine; namespace UniTAS.Patcher.Services.VirtualEnvironment.Input.LegacyInputSystem; public interface IKeyboardStateEnvLegacySystem { - void Hold(Key key); - void Release(Key key); + void Hold(KeyCode keyCode); + void Release(KeyCode keyCode); void Clear(); - bool IsKeyDown(Key key); - bool IsKeyUp(Key key); - bool IsKeyHeld(Key key); + bool IsKeyDown(KeyCode keyCode); + bool IsKeyUp(KeyCode keyCode); + bool IsKeyHeld(KeyCode keyCode); bool AnyKeyHeld { get; } bool AnyKeyDown { get; } } \ No newline at end of file diff --git a/UniTAS/Patcher/Services/VirtualEnvironment/Input/NewInputSystem/IKeyboardStateEnvNewSystem.cs b/UniTAS/Patcher/Services/VirtualEnvironment/Input/NewInputSystem/IKeyboardStateEnvNewSystem.cs index 7ef235e4d..c7c1c9c2d 100644 --- a/UniTAS/Patcher/Services/VirtualEnvironment/Input/NewInputSystem/IKeyboardStateEnvNewSystem.cs +++ b/UniTAS/Patcher/Services/VirtualEnvironment/Input/NewInputSystem/IKeyboardStateEnvNewSystem.cs @@ -1,12 +1,13 @@ using System.Collections.Generic; using UniTAS.Patcher.Models.VirtualEnvironment; +using UnityEngine.InputSystem; namespace UniTAS.Patcher.Services.VirtualEnvironment.Input.NewInputSystem; public interface IKeyboardStateEnvNewSystem { - void Hold(Key key); - void Release(Key key); - List HeldKeys { get; } + void Hold(Key keyCodeWrap); + void Release(Key keyCodeWrap); + List HeldKeys { get; } void Clear(); } \ No newline at end of file diff --git a/UniTAS/Patcher/Utils/InputSystemUtils.cs b/UniTAS/Patcher/Utils/InputSystemUtils.cs new file mode 100644 index 000000000..d4d539b05 --- /dev/null +++ b/UniTAS/Patcher/Utils/InputSystemUtils.cs @@ -0,0 +1,534 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using UnityEngine; +using UnityEngine.InputSystem; + +namespace UniTAS.Patcher.Utils; + +public static class InputSystemUtils +{ + private static readonly Dictionary KeyStringToKeyCodeDict = new() + { + { "backspace", KeyCode.Backspace }, + { "delete", KeyCode.Delete }, + { "tab", KeyCode.Tab }, + { "clear", KeyCode.Clear }, + { "return", KeyCode.Return }, + { "pause", KeyCode.Pause }, + { "escape", KeyCode.Escape }, + { "space", KeyCode.Space }, + { "equals", KeyCode.Equals }, + { "enter", KeyCode.Return }, + { "up", KeyCode.UpArrow }, + { "down", KeyCode.DownArrow }, + { "right", KeyCode.RightArrow }, + { "left", KeyCode.LeftArrow }, + { "insert", KeyCode.Insert }, + { "home", KeyCode.Home }, + { "end", KeyCode.End }, + { "page up", KeyCode.PageUp }, + { "page down", KeyCode.PageDown }, + { "-", KeyCode.Minus }, + { "=", KeyCode.Equals }, + { "!", KeyCode.Exclaim }, + { "@", KeyCode.At }, + { "#", KeyCode.Hash }, + { "$", KeyCode.Dollar }, + { "^", KeyCode.Caret }, + { "&", KeyCode.Ampersand }, + { "*", KeyCode.Asterisk }, + { "(", KeyCode.LeftParen }, + { ")", KeyCode.RightParen }, + { "_", KeyCode.Underscore }, + { "+", KeyCode.Plus }, + { "[", KeyCode.LeftBracket }, + { "]", KeyCode.RightBracket }, + { "`", KeyCode.BackQuote }, + { ";", KeyCode.Semicolon }, + { "'", KeyCode.Quote }, + { "\\", KeyCode.Backslash }, + { ":", KeyCode.Colon }, + { "\"", KeyCode.DoubleQuote }, + { ",", KeyCode.Comma }, + { ".", KeyCode.Period }, + { "/", KeyCode.Slash }, + { "<", KeyCode.Less }, + { ">", KeyCode.Greater }, + { "?", KeyCode.Question }, + { "numlock", KeyCode.Numlock }, + { "caps lock", KeyCode.CapsLock }, + { "scroll lock", KeyCode.ScrollLock }, + { "right shift", KeyCode.RightShift }, + { "left shift", KeyCode.LeftShift }, + { "right ctrl", KeyCode.RightControl }, + { "left ctrl", KeyCode.LeftControl }, + { "right alt", KeyCode.RightAlt }, + { "left alt", KeyCode.LeftAlt }, + { "right cmd", KeyCode.RightApple }, + { "left cmd", KeyCode.LeftApple }, + { "right super", KeyCode.RightWindows }, + { "left super", KeyCode.LeftWindows }, + { "alt gr", KeyCode.AltGr }, + // TODO what is this + // { "compose", KeyCode.Compose }, + { "help", KeyCode.Help }, + { "print screen", KeyCode.Print }, + { "sys req", KeyCode.SysReq }, + { "break", KeyCode.Break }, + { "menu", KeyCode.Menu }, + // TODO those + // { "power", KeyCode.Power }, + // { "euro", KeyCode.Euro }, + // { "undo", KeyCode.Undo }, + }; + + private static readonly Dictionary KeyStringToKeyCodeEnumsExtras = new() + { + { "%", "Percent" }, + { "{", "LeftCurlyBracket" }, + { "}", "RightCurlyBracket" }, + { "~", "Tilde" }, + { "|", "Pipe" }, + }; + + public static void KeyStringToKeys(string key, out KeyCode? keyCode, out Key? newKey) + { + keyCode = KeyCodeParse(key); + newKey = NewKeyParse(key); + + if (keyCode.HasValue) + { + keyCode = keyCode.Value; + if (!newKey.HasValue && keyCode.HasValue) + { + newKey = NewKeyParse(keyCode.Value); + } + + return; + } + + if (newKey.HasValue) + { + newKey = newKey.Value; + if (!keyCode.HasValue && newKey.HasValue) + { + keyCode = KeyCodeParse(newKey.Value); + } + } + } + + private static KeyCode? KeyCodeParse(Key key) + { + switch (key) + { + case Key.None: + return KeyCode.None; + case Key.Backspace: + return KeyCode.Backspace; + case Key.Delete: + return KeyCode.Delete; + case Key.Tab: + return KeyCode.Tab; + case Key.Enter: + return KeyCode.Return; + case Key.Pause: + return KeyCode.Pause; + case Key.Escape: + return KeyCode.Escape; + case Key.Space: + return KeyCode.Space; + case Key.NumpadPeriod: + return KeyCode.KeypadPeriod; + case Key.NumpadDivide: + return KeyCode.KeypadDivide; + case Key.NumpadMultiply: + return KeyCode.KeypadMultiply; + case Key.NumpadMinus: + return KeyCode.KeypadMinus; + case Key.NumpadPlus: + return KeyCode.KeypadPlus; + case Key.NumpadEnter: + return KeyCode.KeypadEnter; + case Key.NumpadEquals: + return KeyCode.KeypadEquals; + case Key.UpArrow: + return KeyCode.UpArrow; + case Key.DownArrow: + return KeyCode.DownArrow; + case Key.RightArrow: + return KeyCode.RightArrow; + case Key.LeftArrow: + return KeyCode.LeftArrow; + case Key.Insert: + return KeyCode.Insert; + case Key.Home: + return KeyCode.Home; + case Key.End: + return KeyCode.End; + case Key.PageUp: + return KeyCode.PageUp; + case Key.PageDown: + return KeyCode.PageDown; + case Key.Quote: + return KeyCode.Quote; + case Key.Comma: + return KeyCode.Comma; + case Key.Minus: + return KeyCode.Minus; + case Key.Period: + return KeyCode.Period; + case Key.Slash: + return KeyCode.Slash; + case Key.Semicolon: + return KeyCode.Semicolon; + case Key.Equals: + return KeyCode.Equals; + case Key.LeftBracket: + return KeyCode.LeftBracket; + case Key.Backslash: + return KeyCode.Backslash; + case Key.RightBracket: + return KeyCode.RightBracket; + case Key.Backquote: + return KeyCode.BackQuote; + case Key.NumLock: + return KeyCode.Numlock; + case Key.CapsLock: + return KeyCode.CapsLock; + case Key.ScrollLock: + return KeyCode.ScrollLock; + case Key.RightShift: + return KeyCode.RightShift; + case Key.LeftShift: + return KeyCode.LeftShift; + case Key.RightCtrl: + return KeyCode.RightControl; + case Key.LeftCtrl: + return KeyCode.LeftControl; + case Key.LeftAlt: + return KeyCode.LeftAlt; + case Key.PrintScreen: + return KeyCode.Print; + case Key.LeftApple: + case Key.RightApple: + case Key.AltGr: + { + // duplicate definitions so handle them here + var keyCodeString = key.ToString(); + + return keyCodeString switch + { + nameof(Key.LeftApple) => KeyCode.LeftApple, + nameof(Key.LeftWindows) => KeyCode.LeftWindows, + nameof(Key.RightApple) => KeyCode.RightApple, + nameof(Key.RightWindows) => KeyCode.RightWindows, + nameof(Key.AltGr) => KeyCode.AltGr, + nameof(Key.RightAlt) => KeyCode.RightAlt, + _ => throw new ArgumentOutOfRangeException() + }; + } + } + + var alphabetRange = GetFromRange(key, Key.A, Key.Z, KeyCode.A, KeyCode.Z); + if (alphabetRange != null) + { + return alphabetRange.Value; + } + + var f1Range = GetFromRange(key, Key.F1, Key.F12, KeyCode.F1, KeyCode.F12); + if (f1Range != null) + { + return f1Range.Value; + } + + var digitRange = GetFromRange(key, Key.Digit0, Key.Digit9, KeyCode.Alpha0, KeyCode.Alpha9); + + if (digitRange != null) + { + // -1 and wrap back if too low + var digitRangeValue = (int)digitRange.Value - 1; + const int digitLowestNew = (int)KeyCode.Alpha1; + if (digitRangeValue < digitLowestNew) + { + digitRangeValue = (int)KeyCode.Alpha9; + } + + return (KeyCode)digitRangeValue; + } + + var keypadRange = GetFromRange(key, Key.Numpad0, Key.Numpad9, KeyCode.Keypad0, KeyCode.Keypad9); + + return keypadRange; + } + + public static KeyCode? KeyCodeParse(string key) + { + if (string.IsNullOrEmpty(key)) return null; + + if (Enum.IsDefined(typeof(KeyCode), key)) + { + return (KeyCode)Enum.Parse(typeof(KeyCode), key); + } + + key = key.ToLower(); + + // alphabet and number + if (key.Length == 1) + { + var keyChar = key[0]; + + // alphabet + if (keyChar is >= 'a' and <= 'z') + { + return (KeyCode)(keyChar - 'a' + (int)KeyCode.A); + } + + // number + if (keyChar is >= '0' and <= '9') + { + return (KeyCode)(keyChar - '0' + (int)KeyCode.Alpha0); + } + } + + // numpad + if (key.StartsWith("[") && key.EndsWith("]")) + { + var keyInner = key.Substring(1, key.Length - 2); + if (int.TryParse(keyInner, NumberStyles.None, new NumberFormatInfo(), out var numpadNum)) + { + if (numpadNum is >= 0 and <= 9) + { + return (KeyCode)(numpadNum + (int)KeyCode.Keypad0); + } + } + + switch (keyInner) + { + case "+": + return KeyCode.KeypadPlus; + case "-": + return KeyCode.KeypadMinus; + case "*": + return KeyCode.KeypadMultiply; + case "/": + return KeyCode.KeypadDivide; + case ".": + return KeyCode.KeypadPeriod; + } + } + + // function key + if (key.StartsWith("f") && int.TryParse(key.Substring(1), NumberStyles.None, new NumberFormatInfo(), + out var numFuncKey)) + { + if (numFuncKey is >= 1 and <= 15) + { + return (KeyCode)(numFuncKey - 1 + (int)KeyCode.F1); + } + } + + // most other key + if (KeyStringToKeyCodeDict.TryGetValue(key, out var keyCode)) return keyCode; + + // out of range enum values + if (!KeyStringToKeyCodeEnumsExtras.TryGetValue(key, out var keyEnumExtra)) return null; + + if (!Enum.IsDefined(typeof(KeyCode), keyEnumExtra)) return null; + + return (KeyCode)Enum.Parse(typeof(KeyCode), keyEnumExtra); + } + + private static Key? NewKeyParse(string key) + { + if (Enum.IsDefined(typeof(Key), key)) + { + return (Key)Enum.Parse(typeof(Key), key); + } + + return null; + } + + private static Key? NewKeyParse(KeyCode keyCode) + { + switch (keyCode) + { + case KeyCode.None: + return Key.None; + case KeyCode.Backspace: + return Key.Backspace; + case KeyCode.Delete: + return Key.Delete; + case KeyCode.Tab: + return Key.Tab; + case KeyCode.Return: + return Key.Enter; + case KeyCode.Pause: + return Key.Pause; + case KeyCode.Escape: + return Key.Escape; + case KeyCode.Space: + return Key.Space; + case KeyCode.KeypadPeriod: + return Key.NumpadPeriod; + case KeyCode.KeypadDivide: + return Key.NumpadDivide; + case KeyCode.KeypadMultiply: + return Key.NumpadMultiply; + case KeyCode.KeypadMinus: + return Key.NumpadMinus; + case KeyCode.KeypadPlus: + return Key.NumpadPlus; + case KeyCode.KeypadEnter: + return Key.NumpadEnter; + case KeyCode.KeypadEquals: + return Key.NumpadEquals; + case KeyCode.UpArrow: + return Key.UpArrow; + case KeyCode.DownArrow: + return Key.DownArrow; + case KeyCode.RightArrow: + return Key.RightArrow; + case KeyCode.LeftArrow: + return Key.LeftArrow; + case KeyCode.Insert: + return Key.Insert; + case KeyCode.Home: + return Key.Home; + case KeyCode.End: + return Key.End; + case KeyCode.PageUp: + return Key.PageUp; + case KeyCode.PageDown: + return Key.PageDown; + case KeyCode.Quote: + return Key.Quote; + case KeyCode.Comma: + return Key.Comma; + case KeyCode.Minus: + return Key.Minus; + case KeyCode.Period: + return Key.Period; + case KeyCode.Slash: + return Key.Slash; + case KeyCode.Semicolon: + return Key.Semicolon; + case KeyCode.Equals: + return Key.Equals; + case KeyCode.LeftBracket: + return Key.LeftBracket; + case KeyCode.Backslash: + return Key.Backslash; + case KeyCode.RightBracket: + return Key.RightBracket; + case KeyCode.BackQuote: + return Key.Backquote; + case KeyCode.Numlock: + return Key.NumLock; + case KeyCode.CapsLock: + return Key.CapsLock; + case KeyCode.ScrollLock: + return Key.ScrollLock; + case KeyCode.RightShift: + return Key.RightShift; + case KeyCode.LeftShift: + return Key.LeftShift; + case KeyCode.RightControl: + return Key.RightCtrl; + case KeyCode.LeftControl: + return Key.LeftCtrl; + case KeyCode.RightAlt: + return Key.RightAlt; + case KeyCode.LeftAlt: + return Key.LeftAlt; + case KeyCode.LeftApple: + return Key.LeftApple; + case KeyCode.LeftWindows: + return Key.LeftWindows; + case KeyCode.RightApple: + return Key.RightApple; + case KeyCode.RightWindows: + return Key.RightWindows; + case KeyCode.AltGr: + return Key.AltGr; + case KeyCode.Print: + return Key.PrintScreen; + } + + var alphabetRange = GetFromRange(keyCode, KeyCode.A, KeyCode.Z, Key.A, + Key.Z); + if (alphabetRange != null) + { + return alphabetRange.Value; + } + + var f1Range = GetFromRange(keyCode, KeyCode.F1, KeyCode.F12, Key.F1, + Key.F12); + if (f1Range != null) + { + return f1Range.Value; + } + + var digitRange = GetFromRange(keyCode, KeyCode.Alpha0, KeyCode.Alpha9, Key.Digit0, + Key.Digit9); + + if (digitRange != null) + { + // -1 and wrap back if too low + var digitRangeValue = (int)digitRange.Value - 1; + const int digitLowestNew = (int)Key.Digit1; + if (digitRangeValue < digitLowestNew) + { + digitRangeValue = (int)Key.Digit9; + } + + return (Key)digitRangeValue; + } + + var keypadRange = GetFromRange(keyCode, KeyCode.Keypad0, KeyCode.Keypad9, Key.Numpad0, + Key.Numpad9); + + return keypadRange; + } + + private static Key? GetFromRange(KeyCode keyCode, KeyCode min, KeyCode max, Key minNew, Key maxNew) + { + var keyCodeInt = (int)keyCode; + var minLegacyInt = (int)min; + var maxLegacyInt = (int)max; + var minNewInt = (int)minNew; + var maxNewInt = (int)maxNew; + + var rangeResult = GetFromRange(keyCodeInt, minLegacyInt, maxLegacyInt, minNewInt, maxNewInt); + return rangeResult.HasValue ? (Key)rangeResult.Value : null; + } + + private static KeyCode? GetFromRange(Key keyCode, Key min, Key max, KeyCode minNew, KeyCode maxNew) + { + var keyCodeInt = (int)keyCode; + var minLegacyInt = (int)min; + var maxLegacyInt = (int)max; + var minNewInt = (int)minNew; + var maxNewInt = (int)maxNew; + + var rangeResult = GetFromRange(keyCodeInt, minLegacyInt, maxLegacyInt, minNewInt, maxNewInt); + return rangeResult.HasValue ? (KeyCode)rangeResult.Value : null; + } + + private static int? GetFromRange(int keyCode, int min, int max, int minNew, int maxNew) + { + // check if out of range key code + if (keyCode < min || keyCode > max) + { + return null; + } + + // check if out of range for new key codes + if (keyCode - min + minNew > maxNew) + { + return null; + } + + return keyCode - min + minNew; + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Utils/LegacyInputSystemUtils.cs b/UniTAS/Patcher/Utils/LegacyInputSystemUtils.cs deleted file mode 100644 index 20a46786d..000000000 --- a/UniTAS/Patcher/Utils/LegacyInputSystemUtils.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using UnityEngine; - -namespace UniTAS.Patcher.Utils; - -public static class LegacyInputSystemUtils -{ - private static readonly Dictionary KeyStringToKeyCodeDict = new() - { - { "backspace", KeyCode.Backspace }, - { "delete", KeyCode.Delete }, - { "tab", KeyCode.Tab }, - { "clear", KeyCode.Clear }, - { "return", KeyCode.Return }, - { "pause", KeyCode.Pause }, - { "escape", KeyCode.Escape }, - { "space", KeyCode.Space }, - { "equals", KeyCode.Equals }, - { "enter", KeyCode.Return }, - { "up", KeyCode.UpArrow }, - { "down", KeyCode.DownArrow }, - { "right", KeyCode.RightArrow }, - { "left", KeyCode.LeftArrow }, - { "insert", KeyCode.Insert }, - { "home", KeyCode.Home }, - { "end", KeyCode.End }, - { "page up", KeyCode.PageUp }, - { "page down", KeyCode.PageDown }, - { "-", KeyCode.Minus }, - { "=", KeyCode.Equals }, - { "!", KeyCode.Exclaim }, - { "@", KeyCode.At }, - { "#", KeyCode.Hash }, - { "$", KeyCode.Dollar }, - { "^", KeyCode.Caret }, - { "&", KeyCode.Ampersand }, - { "*", KeyCode.Asterisk }, - { "(", KeyCode.LeftParen }, - { ")", KeyCode.RightParen }, - { "_", KeyCode.Underscore }, - { "+", KeyCode.Plus }, - { "[", KeyCode.LeftBracket }, - { "]", KeyCode.RightBracket }, - { "`", KeyCode.BackQuote }, - { ";", KeyCode.Semicolon }, - { "'", KeyCode.Quote }, - { "\\", KeyCode.Backslash }, - { ":", KeyCode.Colon }, - { "\"", KeyCode.DoubleQuote }, - { ",", KeyCode.Comma }, - { ".", KeyCode.Period }, - { "/", KeyCode.Slash }, - { "<", KeyCode.Less }, - { ">", KeyCode.Greater }, - { "?", KeyCode.Question }, - { "numlock", KeyCode.Numlock }, - { "caps lock", KeyCode.CapsLock }, - { "scroll lock", KeyCode.ScrollLock }, - { "right shift", KeyCode.RightShift }, - { "left shift", KeyCode.LeftShift }, - { "right ctrl", KeyCode.RightControl }, - { "left ctrl", KeyCode.LeftControl }, - { "right alt", KeyCode.RightAlt }, - { "left alt", KeyCode.LeftAlt }, - { "right cmd", KeyCode.RightApple }, - { "left cmd", KeyCode.LeftApple }, - { "right super", KeyCode.RightWindows }, - { "left super", KeyCode.LeftWindows }, - { "alt gr", KeyCode.AltGr }, - // TODO what is this - // { "compose", KeyCode.Compose }, - { "help", KeyCode.Help }, - { "print screen", KeyCode.Print }, - { "sys req", KeyCode.SysReq }, - { "break", KeyCode.Break }, - { "menu", KeyCode.Menu }, - // TODO those - // { "power", KeyCode.Power }, - // { "euro", KeyCode.Euro }, - // { "undo", KeyCode.Undo }, - }; - - private static readonly Dictionary KeyStringToKeyCodeEnumsExtras = new() - { - { "%", "Percent" }, - { "{", "LeftCurlyBracket" }, - { "}", "RightCurlyBracket" }, - { "~", "Tilde" }, - { "|", "Pipe" }, - }; - - public static bool KeyStringToKeyCode(string key, out KeyCode keyCode) - { - // all lower case, not allowed - keyCode = default; - if (string.IsNullOrEmpty(key)) return false; - - // alphabet and number - if (key.Length == 1) - { - var keyChar = key[0]; - - // alphabet - if (keyChar is >= 'a' and <= 'z') - { - keyCode = (KeyCode)(keyChar - 'a' + (int)KeyCode.A); - return true; - } - - // number - if (keyChar is >= '0' and <= '9') - { - keyCode = (KeyCode)(keyChar - '0' + (int)KeyCode.Alpha0); - return true; - } - } - - // numpad - if (key.StartsWith("[") && key.EndsWith("]")) - { - var keyInner = key.Substring(1, key.Length - 2); - if (int.TryParse(keyInner, NumberStyles.None, new NumberFormatInfo(), out var numpadNum)) - { - if (numpadNum is < 0 or > 9) return false; - keyCode = (KeyCode)(numpadNum + (int)KeyCode.Keypad0); - return true; - } - - switch (keyInner) - { - case "+": - keyCode = KeyCode.KeypadPlus; - break; - case "-": - keyCode = KeyCode.KeypadMinus; - break; - case "*": - keyCode = KeyCode.KeypadMultiply; - break; - case "/": - keyCode = KeyCode.KeypadDivide; - break; - case ".": - keyCode = KeyCode.KeypadPeriod; - break; - default: - return false; - } - - return true; - } - - // function key - if (key.StartsWith("f") && int.TryParse(key.Substring(1), NumberStyles.None, new NumberFormatInfo(), - out var numFuncKey)) - { - if (numFuncKey is < 1 or > 15) return false; - keyCode = (KeyCode)(numFuncKey - 1 + (int)KeyCode.F1); - return true; - } - - // most other key - if (KeyStringToKeyCodeDict.TryGetValue(key, out keyCode)) return true; - - // out of range enum values - if (KeyStringToKeyCodeEnumsExtras.TryGetValue(key, out var keyEnumExtra)) - { - if (!Enum.IsDefined(typeof(KeyCode), keyEnumExtra)) return false; - - keyCode = (KeyCode)Enum.Parse(typeof(KeyCode), keyEnumExtra); - return true; - } - - return false; - } -} \ No newline at end of file From 4e1382e1926ef74a9169545706b10b5f8446aad1 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Wed, 20 Sep 2023 23:35:02 +0100 Subject: [PATCH 029/116] moved to appropriate namespace --- UniTAS/Patcher/Implementations/{ => GUI}/CursorOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename UniTAS/Patcher/Implementations/{ => GUI}/CursorOverlay.cs (97%) diff --git a/UniTAS/Patcher/Implementations/CursorOverlay.cs b/UniTAS/Patcher/Implementations/GUI/CursorOverlay.cs similarity index 97% rename from UniTAS/Patcher/Implementations/CursorOverlay.cs rename to UniTAS/Patcher/Implementations/GUI/CursorOverlay.cs index 951db02d9..7003f3905 100644 --- a/UniTAS/Patcher/Implementations/CursorOverlay.cs +++ b/UniTAS/Patcher/Implementations/GUI/CursorOverlay.cs @@ -10,7 +10,7 @@ using UniTAS.Patcher.Utils; using UnityEngine; -namespace UniTAS.Patcher.Implementations; +namespace UniTAS.Patcher.Implementations.GUI; [Singleton] [ExcludeRegisterIfTesting] From 791fcc18f488e6e3c49d955816a8f0ee7a2ea46c Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Wed, 20 Sep 2023 23:41:23 +0100 Subject: [PATCH 030/116] updated api to make sense --- .../GUI/Overlays/FrameCountOverlay.cs | 6 ++--- .../GUI/Overlays/MovieEndStatus.cs | 7 +++--- .../GUI/Overlays/TimeOverlay.cs | 24 ++++++++++--------- .../Patcher/Interfaces/GUI/BuiltInOverlay.cs | 15 ++++++------ 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/UniTAS/Patcher/Implementations/GUI/Overlays/FrameCountOverlay.cs b/UniTAS/Patcher/Implementations/GUI/Overlays/FrameCountOverlay.cs index 22f36a634..cf48814e8 100644 --- a/UniTAS/Patcher/Implementations/GUI/Overlays/FrameCountOverlay.cs +++ b/UniTAS/Patcher/Implementations/GUI/Overlays/FrameCountOverlay.cs @@ -22,7 +22,7 @@ public FrameCountOverlay(IConfig config, IDrawing drawing) : base(config, drawin } protected override AnchoredOffset DefaultOffset => new(0, 0, 0, 0); - protected override string ConfigValue => "FrameCount"; + protected override string ConfigName => "FrameCount"; public void OnMovieRunningStatusChange(bool running) { @@ -38,14 +38,14 @@ public void OnMovieRunningStatusChange(bool running) } } - protected override void Update() + protected override string Update() { if (_update) { _frameCount++; } - Text = $"Frame: {_frameCount.ToString()}, Fixed Frame: {_fixedFrameCount.ToString()}"; + return $"Frame: {_frameCount.ToString()}, Fixed Frame: {_fixedFrameCount.ToString()}"; } public void FixedUpdateActual() diff --git a/UniTAS/Patcher/Implementations/GUI/Overlays/MovieEndStatus.cs b/UniTAS/Patcher/Implementations/GUI/Overlays/MovieEndStatus.cs index 0b96ad72b..c995e43bf 100644 --- a/UniTAS/Patcher/Implementations/GUI/Overlays/MovieEndStatus.cs +++ b/UniTAS/Patcher/Implementations/GUI/Overlays/MovieEndStatus.cs @@ -8,7 +8,6 @@ namespace UniTAS.Patcher.Implementations.GUI.Overlays; [Singleton] -[ForceInstantiate] [ExcludeRegisterIfTesting] public class MovieEndStatus : BuiltInOverlay, IOnMovieRunningStatusChange { @@ -20,13 +19,13 @@ public MovieEndStatus(IConfig config, IDrawing drawing) : base(config, drawing) } protected override AnchoredOffset DefaultOffset => new(1, 1, 0, 0); - protected override string ConfigValue => "MovieEndStatus"; + protected override string ConfigName => "MovieEndStatus"; protected override int DefaultFontSize => 20; - protected override void Update() + protected override string Update() { _messageDisplayLeft -= Time.deltaTime; - Text = _messageDisplayLeft <= 0 ? "" : "Movie End"; + return _messageDisplayLeft <= 0 ? "" : "Movie End"; } public void OnMovieRunningStatusChange(bool running) diff --git a/UniTAS/Patcher/Implementations/GUI/Overlays/TimeOverlay.cs b/UniTAS/Patcher/Implementations/GUI/Overlays/TimeOverlay.cs index a48c4ed9d..9ce33f95b 100644 --- a/UniTAS/Patcher/Implementations/GUI/Overlays/TimeOverlay.cs +++ b/UniTAS/Patcher/Implementations/GUI/Overlays/TimeOverlay.cs @@ -8,53 +8,55 @@ namespace UniTAS.Patcher.Implementations.GUI.Overlays; -[ForceInstantiate] [Singleton] [ExcludeRegisterIfTesting] public class TimeOverlay : BuiltInOverlay, IOnMovieRunningStatusChange { private bool _update; private readonly ITimeEnv _timeEnv; + private string _text; public TimeOverlay(IConfig config, IDrawing drawing, ITimeEnv timeEnv) : base(config, drawing) { _timeEnv = timeEnv; - Text = "0.000"; + _text = "0.000"; } protected override AnchoredOffset DefaultOffset => new(0, 0, 0, 30); - protected override string ConfigValue => "Time"; + protected override string ConfigName => "Time"; - protected override void Update() + protected override string Update() { - if (!_update) return; + if (!_update) return _text; var time = TimeSpan.FromSeconds(_timeEnv.SecondsSinceStartUp); var hour = time.Hours > 0; - Text = ""; + _text = ""; if (hour) { - Text += $"{time.Hours}:"; + _text += $"{time.Hours}:"; if (time.Minutes < 10) { - Text += "0"; + _text += "0"; } } var minute = time.Minutes > 0 || hour; if (minute) { - Text += $"{time.Minutes}:"; + _text += $"{time.Minutes}:"; if (time.Seconds < 10) { - Text += "0"; + _text += "0"; } } - Text += $"{time.Seconds}.{time.Milliseconds:D3}"; + _text += $"{time.Seconds}.{time.Milliseconds:D3}"; + + return _text; } public void OnMovieRunningStatusChange(bool running) diff --git a/UniTAS/Patcher/Interfaces/GUI/BuiltInOverlay.cs b/UniTAS/Patcher/Interfaces/GUI/BuiltInOverlay.cs index a1eac2834..861e33450 100644 --- a/UniTAS/Patcher/Interfaces/GUI/BuiltInOverlay.cs +++ b/UniTAS/Patcher/Interfaces/GUI/BuiltInOverlay.cs @@ -18,12 +18,10 @@ public abstract class BuiltInOverlay : IOnUpdateUnconditional, IOverlayVisibleTo private ConfigEntry _enabled; private ConfigEntry _fontSize; - protected abstract string ConfigValue { get; } + protected abstract string ConfigName { get; } private readonly IDrawing _drawing; - protected string Text { get; set; } - public bool Enabled { get; set; } = true; protected BuiltInOverlay(IConfig config, IDrawing drawing) @@ -34,7 +32,7 @@ protected BuiltInOverlay(IConfig config, IDrawing drawing) private void Init(IConfig config) { - var entry = $"BuiltInOverlays.{ConfigValue}"; + var entry = $"BuiltInOverlays.{ConfigName}"; _anchorX = config.ConfigFile.Bind(entry, "AnchorX", DefaultOffset.AnchorX, "Anchor X position. 0 is left, 1 is right."); _anchorY = config.ConfigFile.Bind(entry, "AnchorY", DefaultOffset.AnchorY, @@ -48,13 +46,14 @@ private void Init(IConfig config) public void UpdateUnconditional() { if (!Enabled || !_enabled.Value) return; - Update(); - _drawing.PrintText(new(_anchorX.Value, _anchorY.Value, _offsetX.Value, _offsetY.Value), Text, - _fontSize.Value); + var text = Update(); + if (text == null) return; + _drawing.PrintText(new(_anchorX.Value, _anchorY.Value, _offsetX.Value, _offsetY.Value), text, _fontSize.Value); } /// /// Update that happens before the text is drawn /// - protected abstract void Update(); + /// Text to draw. Return null to skip drawing. + protected abstract string Update(); } \ No newline at end of file From a6ad8b3aa937a9768e519e4fd6d39774712ab0dc Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Wed, 20 Sep 2023 23:45:54 +0100 Subject: [PATCH 031/116] added mouse pos overlay --- .../GUI/Overlays/MouseCoordsOverlay.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 UniTAS/Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs diff --git a/UniTAS/Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs b/UniTAS/Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs new file mode 100644 index 000000000..f3526cbbc --- /dev/null +++ b/UniTAS/Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs @@ -0,0 +1,25 @@ +using UniTAS.Patcher.Interfaces.DependencyInjection; +using UniTAS.Patcher.Interfaces.GUI; +using UniTAS.Patcher.Models.GUI; +using UniTAS.Patcher.Services; +using UnityEngine; + +namespace UniTAS.Patcher.Implementations.GUI.Overlays; + +[Singleton] +[ExcludeRegisterIfTesting] +public class MouseCoordsOverlay : BuiltInOverlay +{ + public MouseCoordsOverlay(IConfig config, IDrawing drawing) : base(config, drawing) + { + } + + protected override AnchoredOffset DefaultOffset { get; } = new(0, 0, 0, 60); + protected override string ConfigName => "MouseCoords"; + + protected override string Update() + { + var mousePos = Input.mousePosition; + return $"Mouse X: {mousePos.x}, Y: {Screen.height - mousePos.y}"; + } +} \ No newline at end of file From 8fa387c4b5b18cd88d5fdf032c09fcb5637b9c09 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 22 Sep 2023 03:42:45 +0100 Subject: [PATCH 032/116] added implicit use attribute --- UniTAS/Patcher/Interfaces/Patches/PatchTypes/PatchType.cs | 2 ++ UniTAS/Patcher/Patcher.csproj | 1 + 2 files changed, 3 insertions(+) diff --git a/UniTAS/Patcher/Interfaces/Patches/PatchTypes/PatchType.cs b/UniTAS/Patcher/Interfaces/Patches/PatchTypes/PatchType.cs index 9711d5118..214527af1 100644 --- a/UniTAS/Patcher/Interfaces/Patches/PatchTypes/PatchType.cs +++ b/UniTAS/Patcher/Interfaces/Patches/PatchTypes/PatchType.cs @@ -1,7 +1,9 @@ using System; +using JetBrains.Annotations; namespace UniTAS.Patcher.Interfaces.Patches.PatchTypes; +[MeansImplicitUse] public abstract class PatchType : Attribute { public int Priority { get; } diff --git a/UniTAS/Patcher/Patcher.csproj b/UniTAS/Patcher/Patcher.csproj index a8badb3a9..98319feba 100644 --- a/UniTAS/Patcher/Patcher.csproj +++ b/UniTAS/Patcher/Patcher.csproj @@ -54,6 +54,7 @@ + From f32548b1db0559573902532da6ee457c60dbb549 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 23 Sep 2023 02:03:01 +0100 Subject: [PATCH 033/116] addition of event for pre game restart --- .../Implementations/GameRestart/GameRestart.cs | 18 +++++++++--------- UniTAS/Patcher/Services/IGameRestart.cs | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/UniTAS/Patcher/Implementations/GameRestart/GameRestart.cs b/UniTAS/Patcher/Implementations/GameRestart/GameRestart.cs index 40b5bd85f..2fb4fbfdd 100644 --- a/UniTAS/Patcher/Implementations/GameRestart/GameRestart.cs +++ b/UniTAS/Patcher/Implementations/GameRestart/GameRestart.cs @@ -31,8 +31,6 @@ public class GameRestart : IGameRestart, IOnAwakeUnconditional, IOnEnableUncondi private readonly IUpdateInvokeOffset _updateInvokeOffset; private readonly IObjectTracker _objectTracker; - private readonly IOnPreGameRestart[] _onPreGameRestart; - private readonly IStaticFieldManipulator _staticFieldManipulator; private readonly ITimeEnv _timeEnv; @@ -50,7 +48,6 @@ public GameRestart(ISyncFixedUpdateCycle syncFixedUpdate, ISceneWrapper sceneWra _sceneWrapper = sceneWrapper; _monoBehaviourController = monoBehaviourController; _logger = logger; - _onPreGameRestart = onPreGameRestart; _staticFieldManipulator = staticFieldManipulator; _timeEnv = timeEnv; _finalizeSuppressor = finalizeSuppressor; @@ -66,6 +63,11 @@ public GameRestart(ISyncFixedUpdateCycle syncFixedUpdate, ISceneWrapper sceneWra { OnGameRestart += gameRestart.OnGameRestart; } + + foreach (var gameRestart in onPreGameRestart) + { + OnPreGameRestart += gameRestart.OnPreGameRestart; + } } /// @@ -94,7 +96,7 @@ public void SoftRestart(DateTime time) _logger.LogInfo("Starting soft restart"); - OnPreGameRestart(); + InvokeOnPreGameRestart(); _pendingRestart = true; _softRestartTime = time; @@ -118,18 +120,16 @@ public void SoftRestart(DateTime time) public event GameRestartResume OnGameRestartResume; public event Services.GameRestart OnGameRestart; + public event Action OnPreGameRestart; protected virtual void InvokeOnGameRestartResume(bool preMonoBehaviourResume) { OnGameRestartResume?.Invoke(_softRestartTime, preMonoBehaviourResume); } - private void OnPreGameRestart() + private void InvokeOnPreGameRestart() { - foreach (var gameRestart in _onPreGameRestart) - { - gameRestart.OnPreGameRestart(); - } + OnPreGameRestart?.Invoke(); } private void SoftRestartOperation() diff --git a/UniTAS/Patcher/Services/IGameRestart.cs b/UniTAS/Patcher/Services/IGameRestart.cs index a0aa6006b..1f644456a 100644 --- a/UniTAS/Patcher/Services/IGameRestart.cs +++ b/UniTAS/Patcher/Services/IGameRestart.cs @@ -7,6 +7,7 @@ public interface IGameRestart void SoftRestart(DateTime time); event GameRestartResume OnGameRestartResume; event GameRestart OnGameRestart; + event Action OnPreGameRestart; } public delegate void GameRestartResume(DateTime startupTime, bool preMonoBehaviourResume); From 69ff8016b32a0e2d9fe5da42bddca903ba48c19e Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 23 Sep 2023 02:58:47 +0100 Subject: [PATCH 034/116] implicit use attribute added --- .../DependencyInjection/DependencyInjectionAttribute.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/UniTAS/Patcher/Interfaces/DependencyInjection/DependencyInjectionAttribute.cs b/UniTAS/Patcher/Interfaces/DependencyInjection/DependencyInjectionAttribute.cs index 36c097ab9..95ab2d262 100644 --- a/UniTAS/Patcher/Interfaces/DependencyInjection/DependencyInjectionAttribute.cs +++ b/UniTAS/Patcher/Interfaces/DependencyInjection/DependencyInjectionAttribute.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; +using JetBrains.Annotations; namespace UniTAS.Patcher.Interfaces.DependencyInjection; +[MeansImplicitUse] public abstract class DependencyInjectionAttribute : Attribute { public abstract IEnumerable GetRegisterInfos(Type type, Type[] allTypes, bool isTesting); From 397e9c18c1601e6bfb4583a22b875dc4a8b4fc42 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 21 Sep 2023 02:05:38 +0100 Subject: [PATCH 035/116] fixed mouse pos being inverted --- .../Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs b/UniTAS/Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs index f3526cbbc..76f6c2de5 100644 --- a/UniTAS/Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs +++ b/UniTAS/Patcher/Implementations/GUI/Overlays/MouseCoordsOverlay.cs @@ -20,6 +20,6 @@ public MouseCoordsOverlay(IConfig config, IDrawing drawing) : base(config, drawi protected override string Update() { var mousePos = Input.mousePosition; - return $"Mouse X: {mousePos.x}, Y: {Screen.height - mousePos.y}"; + return $"Mouse X: {mousePos.x}, Y: {mousePos.y}"; } } \ No newline at end of file From 1ebb7e84ffcfd295469eaf2083a4eb338ffc9344 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 21 Sep 2023 12:19:14 +0100 Subject: [PATCH 036/116] create plugins dir on init --- UniTAS/Patcher/Entry.cs | 5 ++++- UniTAS/Patcher/Utils/BepInExUtils.cs | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 UniTAS/Patcher/Utils/BepInExUtils.cs diff --git a/UniTAS/Patcher/Entry.cs b/UniTAS/Patcher/Entry.cs index 46e04b224..2f3377fd3 100644 --- a/UniTAS/Patcher/Entry.cs +++ b/UniTAS/Patcher/Entry.cs @@ -23,9 +23,12 @@ public static class Entry public static void Initialize() { LoggingUtils.InitDiskLogger(); + StaticLogger.Log.LogInfo("Initializing UniTAS"); + + BepInExUtils.GenerateMissingDirs(); StaticLogger.Log.LogInfo($"Found {PreloadPatcherProcessor.PreloadPatchers.Length} preload patchers"); - StaticLogger.Log.LogInfo($"Target dlls: {string.Join(", ", PreloadPatcherProcessor.TargetDLLs.ToArray())}"); + StaticLogger.Log.LogDebug($"Target dlls: {string.Join(", ", PreloadPatcherProcessor.TargetDLLs.ToArray())}"); } // Patches the assemblies diff --git a/UniTAS/Patcher/Utils/BepInExUtils.cs b/UniTAS/Patcher/Utils/BepInExUtils.cs new file mode 100644 index 000000000..aa3891b95 --- /dev/null +++ b/UniTAS/Patcher/Utils/BepInExUtils.cs @@ -0,0 +1,23 @@ +using System.IO; +using BepInEx; + +namespace UniTAS.Patcher.Utils; + +public static class BepInExUtils +{ + public static void GenerateMissingDirs() + { + var dirs = new[] + { + Paths.PluginPath + }; + + foreach (var dir in dirs) + { + if (Directory.Exists(dir)) continue; + + StaticLogger.Log.LogInfo($"Creating missing BepInEx directory {dir}"); + Directory.CreateDirectory(dir); + } + } +} \ No newline at end of file From d869100c9d796ad27a316e225057afcbdb9f2cb8 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 23 Sep 2023 05:07:01 +0100 Subject: [PATCH 037/116] added UniTAS patcher change detection --- .../ManualServices/UniTASSha256Info.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 UniTAS/Patcher/ManualServices/UniTASSha256Info.cs diff --git a/UniTAS/Patcher/ManualServices/UniTASSha256Info.cs b/UniTAS/Patcher/ManualServices/UniTASSha256Info.cs new file mode 100644 index 000000000..abfd91139 --- /dev/null +++ b/UniTAS/Patcher/ManualServices/UniTASSha256Info.cs @@ -0,0 +1,33 @@ +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using BepInEx; +using UniTAS.Patcher.Utils; + +namespace UniTAS.Patcher.ManualServices; + +public static class UniTASSha256Info +{ + static UniTASSha256Info() + { + var oldRunSha256Path = Path.Combine(UniTASPaths.Cache, "UniTASPatcher.sha256"); + var currentRunSha256Path = Utility.CombinePaths(Paths.PatcherPluginPath, "UniTAS", "UniTAS.Patcher.dll"); + var currentRunSha256Stream = File.OpenRead(currentRunSha256Path); + using var sha256 = SHA256.Create(); + var currentRunSha256 = sha256.ComputeHash(currentRunSha256Stream); + + if (File.Exists(oldRunSha256Path)) + { + var oldRunSha256 = File.ReadAllBytes(oldRunSha256Path); + UniTASChanged = !currentRunSha256.SequenceEqual(oldRunSha256); + } + else + { + UniTASChanged = true; + } + + File.WriteAllBytes(oldRunSha256Path, currentRunSha256); + } + + public static bool UniTASChanged { get; } +} \ No newline at end of file From c68c488d8f5f754371e83dbf588b023b4dc7cac7 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 23 Sep 2023 05:26:54 +0100 Subject: [PATCH 038/116] added preload patcher caching --- UniTAS/Patcher/Entry.cs | 70 +++++++++++++++++++++++++++++++++++ UniTAS/Patcher/Utils/Paths.cs | 10 +++++ 2 files changed, 80 insertions(+) diff --git a/UniTAS/Patcher/Entry.cs b/UniTAS/Patcher/Entry.cs index 2f3377fd3..92feebf4c 100644 --- a/UniTAS/Patcher/Entry.cs +++ b/UniTAS/Patcher/Entry.cs @@ -1,8 +1,11 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; +using System.Security.Cryptography; using Mono.Cecil; using UniTAS.Patcher.Implementations; +using UniTAS.Patcher.ManualServices; using UniTAS.Patcher.Models.DependencyInjection; using UniTAS.Patcher.Utils; @@ -27,6 +30,16 @@ public static void Initialize() BepInExUtils.GenerateMissingDirs(); + if (UniTASSha256Info.UniTASChanged) + { + // cached assemblies are invalid as UniTAS has changed + // .sha256 files + foreach (var file in Directory.GetFiles(UniTASPaths.AssemblyCache, "*.sha256")) + { + File.Delete(file); + } + } + StaticLogger.Log.LogInfo($"Found {PreloadPatcherProcessor.PreloadPatchers.Length} preload patchers"); StaticLogger.Log.LogDebug($"Target dlls: {string.Join(", ", PreloadPatcherProcessor.TargetDLLs.ToArray())}"); } @@ -38,6 +51,13 @@ public static void Patch(ref AssemblyDefinition assembly) var assemblyNameWithDll = $"{assembly.Name.Name}.dll"; StaticLogger.Log.LogDebug($"Received assembly {assemblyNameWithDll} for patching"); + // check cache validity and patch if needed + if (TargetAssemblyCacheIsValid(ref assembly)) + { + StaticLogger.Log.LogDebug("Skipping patching as it is cached"); + return; + } + foreach (var patcher in PreloadPatcherProcessor.PreloadPatchers) { // only patch the assembly if it's in the list of target assemblies @@ -45,6 +65,49 @@ public static void Patch(ref AssemblyDefinition assembly) StaticLogger.Log.LogInfo($"Patching {assemblyNameWithDll} with {patcher.GetType().Name}"); patcher.Patch(ref assembly); } + + // save patched assembly to cache + SavePatchedAssemblyToCache(assembly); + } + + private static void SavePatchedAssemblyToCache(AssemblyDefinition assembly) + { + var dllCachePath = Path.Combine(UniTASPaths.AssemblyCache, $"{assembly.Name.Name}.dll"); + assembly.Write(dllCachePath); + } + + private static bool TargetAssemblyCacheIsValid(ref AssemblyDefinition assembly) + { + var targetDllPath = assembly.MainModule.FileName; + var assemblyNameWithDll = $"{assembly.Name.Name}.dll"; + + using var targetDllOriginal = File.OpenRead(targetDllPath); + using var dllSha256 = SHA256.Create(); + var hash = dllSha256.ComputeHash(targetDllOriginal); + + var cachedDllSha256Path = Path.Combine(UniTASPaths.AssemblyCache, $"{assemblyNameWithDll}.sha256"); + if (!File.Exists(cachedDllSha256Path)) + { + File.WriteAllBytes(cachedDllSha256Path, hash); + return false; + } + + var cachedHash = File.ReadAllBytes(cachedDllSha256Path); + if (!hash.SequenceEqual(cachedHash)) + { + File.WriteAllBytes(cachedDllSha256Path, hash); + return false; + } + + var cachedDllPath = Path.Combine(UniTASPaths.AssemblyCache, assemblyNameWithDll); + if (!File.Exists(cachedDllPath)) + { + return false; + } + + assembly = AssemblyDefinition.ReadAssembly(cachedDllPath); + + return true; } [SuppressMessage("ReSharper", "UnusedMember.Global")] @@ -52,5 +115,12 @@ public static void Finish() { StaticLogger.Log.LogInfo("Finished preload patcher!"); ContainerStarter.Init(RegisterTiming.Entry); + + // TODO way to save dlls to cache + + // TODO add mechanism to skip patching the dll if that dll is cached and is the same + + // TODO detection for when the target dll is changed, cache should be invalidated + // TODO detection for when UniTAS build is changed, cache should be invalidated } } \ No newline at end of file diff --git a/UniTAS/Patcher/Utils/Paths.cs b/UniTAS/Patcher/Utils/Paths.cs index 2ad6bee8f..980a377ca 100644 --- a/UniTAS/Patcher/Utils/Paths.cs +++ b/UniTAS/Patcher/Utils/Paths.cs @@ -1,8 +1,18 @@ +using System.IO; using BepInEx; namespace UniTAS.Patcher.Utils; public static class UniTASPaths { + static UniTASPaths() + { + Directory.CreateDirectory(AssemblyCache); + } + + private static string UniTASBase { get; } = Path.Combine(Paths.GameRootPath, "UniTAS"); + public static string Resources { get; } = Utility.CombinePaths(Paths.PatcherPluginPath, "UniTAS", "Resources"); + public static string Cache { get; } = Path.Combine(UniTASBase, "Cache"); + public static string AssemblyCache { get; } = Path.Combine(Cache, "Assemblies"); } \ No newline at end of file From 6a7cc4ccda45fcb9dfd29186038b552bc0a02f60 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 23 Sep 2023 05:27:11 +0100 Subject: [PATCH 039/116] removed TODOs --- UniTAS/Patcher/Entry.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/UniTAS/Patcher/Entry.cs b/UniTAS/Patcher/Entry.cs index 92feebf4c..390ec85dd 100644 --- a/UniTAS/Patcher/Entry.cs +++ b/UniTAS/Patcher/Entry.cs @@ -115,12 +115,5 @@ public static void Finish() { StaticLogger.Log.LogInfo("Finished preload patcher!"); ContainerStarter.Init(RegisterTiming.Entry); - - // TODO way to save dlls to cache - - // TODO add mechanism to skip patching the dll if that dll is cached and is the same - - // TODO detection for when the target dll is changed, cache should be invalidated - // TODO detection for when UniTAS build is changed, cache should be invalidated } } \ No newline at end of file From b789841a367e5abfae0c920d1f981a8117f293d5 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 23 Sep 2023 05:33:37 +0100 Subject: [PATCH 040/116] updated --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cca1e1d8c..b9ad68124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 # [Unreleased] +## Changed + +### Performance + +- Loading should be faster if you've opened the game before + ## Fixed ### Games From 31340d0601792424fd2fe9835b6d6b4f18c9a4e4 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 20:26:35 +0100 Subject: [PATCH 041/116] added implicit instantiates --- UniTAS/Patcher.Tests/Utils/EitherTests.cs | 20 ++++++++++++++++++++ UniTAS/Patcher/Models/Utils/Either.cs | 3 +++ 2 files changed, 23 insertions(+) diff --git a/UniTAS/Patcher.Tests/Utils/EitherTests.cs b/UniTAS/Patcher.Tests/Utils/EitherTests.cs index b55896ac1..d7484d187 100644 --- a/UniTAS/Patcher.Tests/Utils/EitherTests.cs +++ b/UniTAS/Patcher.Tests/Utils/EitherTests.cs @@ -24,4 +24,24 @@ public void Right() Assert.Equal("test", either.Right); Assert.Throws(() => either.Left); } + + [Fact] + public void ImplicitLeft() + { + Either either = 5; + Assert.True(either.IsLeft); + Assert.False(either.IsRight); + Assert.Equal(5, either.Left); + Assert.Throws(() => either.Right); + } + + [Fact] + public void ImplicitRight() + { + Either either = "test"; + Assert.False(either.IsLeft); + Assert.True(either.IsRight); + Assert.Equal("test", either.Right); + Assert.Throws(() => either.Left); + } } \ No newline at end of file diff --git a/UniTAS/Patcher/Models/Utils/Either.cs b/UniTAS/Patcher/Models/Utils/Either.cs index 80802b027..f198fb383 100644 --- a/UniTAS/Patcher/Models/Utils/Either.cs +++ b/UniTAS/Patcher/Models/Utils/Either.cs @@ -26,4 +26,7 @@ public Either(TRight right) public TLeft Left => IsLeft ? _left : throw new InvalidOperationException("Either is not left"); public TRight Right => !IsLeft ? _right : throw new InvalidOperationException("Either is not right"); + + public static implicit operator Either(TLeft left) => new(left); + public static implicit operator Either(TRight right) => new(right); } \ No newline at end of file From deea5636128237f1b768d56798b748c2bd7d1cf4 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 21:46:32 +0100 Subject: [PATCH 042/116] added scene load event callback --- .../Interfaces/Events/UnityEvents/IOnSceneLoad.cs | 9 +++++++++ .../AsyncOperationTracker.cs | 10 +++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 UniTAS/Patcher/Interfaces/Events/UnityEvents/IOnSceneLoad.cs diff --git a/UniTAS/Patcher/Interfaces/Events/UnityEvents/IOnSceneLoad.cs b/UniTAS/Patcher/Interfaces/Events/UnityEvents/IOnSceneLoad.cs new file mode 100644 index 000000000..b07178abf --- /dev/null +++ b/UniTAS/Patcher/Interfaces/Events/UnityEvents/IOnSceneLoad.cs @@ -0,0 +1,9 @@ +using UniTAS.Patcher.Models.UnitySafeWrappers.SceneManagement; + +namespace UniTAS.Patcher.Interfaces.Events.UnityEvents; + +public interface IOnSceneLoad +{ + void OnSceneLoad(string sceneName, int sceneBuildIndex, LoadSceneMode loadSceneMode, + LocalPhysicsMode localPhysicsMode); +} \ No newline at end of file diff --git a/UniTAS/Patcher/Services/UnityAsyncOperationTracker/AsyncOperationTracker.cs b/UniTAS/Patcher/Services/UnityAsyncOperationTracker/AsyncOperationTracker.cs index 50a4219b4..017888255 100644 --- a/UniTAS/Patcher/Services/UnityAsyncOperationTracker/AsyncOperationTracker.cs +++ b/UniTAS/Patcher/Services/UnityAsyncOperationTracker/AsyncOperationTracker.cs @@ -5,6 +5,7 @@ using HarmonyLib; using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Interfaces.Events.SoftRestart; +using UniTAS.Patcher.Interfaces.Events.UnityEvents; using UniTAS.Patcher.Interfaces.Events.UnityEvents.RunEvenPaused; using UniTAS.Patcher.Models.UnitySafeWrappers.SceneManagement; using UniTAS.Patcher.Services.Logging; @@ -25,6 +26,7 @@ public class AsyncOperationTracker : ISceneLoadTracker, IAssetBundleCreateReques private readonly Dictionary _assetBundleRequests = new(); private readonly ILogger _logger; + private readonly IOnSceneLoad[] _onSceneLoads; private class AssetBundleRequestData { @@ -40,10 +42,11 @@ public AssetBundleRequestData(Object singleResult = null, Object[] multipleResul private readonly ISceneWrapper _sceneWrapper; - public AsyncOperationTracker(ISceneWrapper sceneWrapper, ILogger logger) + public AsyncOperationTracker(ISceneWrapper sceneWrapper, ILogger logger, IOnSceneLoad[] onSceneLoads) { _sceneWrapper = sceneWrapper; _logger = logger; + _onSceneLoads = onSceneLoads; } public void OnPreGameRestart() @@ -91,6 +94,11 @@ public void NewAssetBundleCreateRequest(AsyncOperation asyncOperation, AssetBund public void AsyncSceneLoad(string sceneName, int sceneBuildIndex, LoadSceneMode loadSceneMode, LocalPhysicsMode localPhysicsMode, AsyncOperation asyncOperation) { + foreach (var onSceneLoad in _onSceneLoads) + { + onSceneLoad.OnSceneLoad(sceneName, sceneBuildIndex, loadSceneMode, localPhysicsMode); + } + _logger.LogDebug($"async scene load, {asyncOperation.GetHashCode()}"); _asyncLoads.Add(new(sceneName, sceneBuildIndex, asyncOperation, loadSceneMode, localPhysicsMode)); } From 6183dc51e0582c4b1807912d6f3ac081dee188bb Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 21:53:52 +0100 Subject: [PATCH 043/116] added ScriptableObject save / load state --- .../SaveScriptableObjectStates.Types.cs | 75 +++++++++++++++++++ .../UnityFix/SaveScriptableObjectStates.cs | 74 ++++++++++++++++++ UniTAS/Patcher/Patches/Harmony/ObjectPatch.cs | 14 ++++ .../IInputSystemAddedDeviceTracker.cs | 8 ++ .../INewScriptableObjectTracker.cs | 8 ++ 5 files changed, 179 insertions(+) create mode 100644 UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs create mode 100644 UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs create mode 100644 UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs create mode 100644 UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/INewScriptableObjectTracker.cs diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs new file mode 100644 index 000000000..d443c61a4 --- /dev/null +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs @@ -0,0 +1,75 @@ +using System.Linq; +using System.Reflection; +using HarmonyLib; +using UniTAS.Patcher.Models.Utils; +using UniTAS.Patcher.Services.Logging; +using UniTAS.Patcher.Utils; +using UnityEngine; + +namespace UniTAS.Patcher.Implementations.UnityFix; + +public partial class SaveScriptableObjectStates +{ + private readonly struct StoredState + { + public readonly ScriptableObject ScriptableObject; + + private readonly FieldData[] _savedFields; + + private readonly ILogger _logger; + + public StoredState(ScriptableObject scriptableObject, ILogger logger) + { + ScriptableObject = scriptableObject; + _logger = logger; + + // save + // TODO limit fields that are serializable to save space + var fields = AccessTools.GetDeclaredFields(scriptableObject.GetType()) + .Where(x => !x.IsStatic && !x.IsLiteral); + _savedFields = fields.Select(x => new FieldData(x, scriptableObject)).ToArray(); + } + + public void Load() + { + if (ScriptableObject == null) + { + _logger.LogError("ScriptableObject is null, this should not happen"); + return; + } + + foreach (var savedField in _savedFields) + { + savedField.Load(); + } + } + } + + private readonly struct FieldData + { + private readonly ScriptableObject _instance; + private readonly Either _value; + private readonly FieldInfo _saveField; + + public FieldData(FieldInfo fieldInfo, ScriptableObject instance) + { + _saveField = fieldInfo; + _instance = instance; + + var value = fieldInfo.GetValue(instance); + if (value is Object unityObject) + { + _value = unityObject; + } + else + { + _value = DeepCopy.MakeDeepCopy(value, value.GetType()); + } + } + + public void Load() + { + _saveField.SetValue(_instance, _value.IsLeft ? _value.Left : _value.Right); + } + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs new file mode 100644 index 000000000..5f04096e0 --- /dev/null +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs @@ -0,0 +1,74 @@ +using System.Collections.Generic; +using UniTAS.Patcher.Interfaces.DependencyInjection; +using UniTAS.Patcher.Interfaces.Events.SoftRestart; +using UniTAS.Patcher.Interfaces.Events.UnityEvents; +using UniTAS.Patcher.Models.UnitySafeWrappers.SceneManagement; +using UniTAS.Patcher.Services.Logging; +using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; +using UniTAS.Patcher.Utils; +using UnityEngine; + +namespace UniTAS.Patcher.Implementations.UnityFix; + +[Singleton] +public partial class SaveScriptableObjectStates : IOnPreGameRestart, IOnSceneLoad, INewScriptableObjectTracker +{ + private readonly List _storedStates = new(); + + private readonly ILogger _logger; + + public SaveScriptableObjectStates(ILogger logger) + { + _logger = logger; + + SaveAll(); + } + + private void SaveAll() + { + _logger.LogDebug("Saving all ScriptableObject states"); + + var allScriptableObjects = ResourcesUtils.FindObjectsOfTypeAll(); + + foreach (var obj in allScriptableObjects) + { + Save(obj); + } + } + + private void Save(ScriptableObject obj) + { + foreach (var x in _storedStates) + { + if (x.ScriptableObject == obj) return; + } + + _storedStates.Add(new(obj, _logger)); + } + + public void NewScriptableObject(ScriptableObject scriptableObject) + { + Save(scriptableObject); + } + + public void OnPreGameRestart() + { + _logger.LogDebug("Loading all ScriptableObject states"); + + foreach (var storedState in _storedStates) + { + storedState.Load(); + } + } + + public void OnSceneLoad(string sceneName, int sceneBuildIndex, LoadSceneMode loadSceneMode, + LocalPhysicsMode localPhysicsMode) + { + if (loadSceneMode == LoadSceneMode.Single) + { + _storedStates.Clear(); + } + + SaveAll(); + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Patches/Harmony/ObjectPatch.cs b/UniTAS/Patcher/Patches/Harmony/ObjectPatch.cs index 08efb333c..d37aac464 100644 --- a/UniTAS/Patcher/Patches/Harmony/ObjectPatch.cs +++ b/UniTAS/Patcher/Patches/Harmony/ObjectPatch.cs @@ -1,10 +1,12 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using HarmonyLib; using UniTAS.Patcher.Interfaces.Patches.PatchTypes; using UniTAS.Patcher.MonoBehaviourScripts; +using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; using UniTAS.Patcher.Utils; using UnityEngine; using Object = UnityEngine.Object; @@ -14,6 +16,7 @@ namespace UniTAS.Patcher.Patches.Harmony; [RawPatch] +[SuppressMessage("ReSharper", "InconsistentNaming")] // ReSharper disable once ClassNeverInstantiated.Global public class ObjectPatch { @@ -55,6 +58,9 @@ private static Exception Cleanup(MethodBase original, Exception ex) } } + private static readonly INewScriptableObjectTracker NewScriptableObjectTracker = + ContainerStarter.Kernel.GetInstance(); + [HarmonyPatch] private class PreventPluginInstantiation { @@ -84,6 +90,14 @@ private static bool Prefix(Object data) return data is not MonoBehaviourUpdateInvoker; } + private static void Postfix(Object __result) + { + if (__result is ScriptableObject scriptableObject) + { + NewScriptableObjectTracker.NewScriptableObject(scriptableObject); + } + } + // ReSharper disable once UnusedParameter.Local private static Exception Cleanup(MethodBase original, Exception ex) { diff --git a/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs b/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs new file mode 100644 index 000000000..7bc0df5bd --- /dev/null +++ b/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs @@ -0,0 +1,8 @@ +using UnityEngine.InputSystem; + +namespace UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; + +public interface IInputSystemAddedDeviceTracker +{ + void AddDevice(InputDevice device); +} \ No newline at end of file diff --git a/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/INewScriptableObjectTracker.cs b/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/INewScriptableObjectTracker.cs new file mode 100644 index 000000000..4df19683f --- /dev/null +++ b/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/INewScriptableObjectTracker.cs @@ -0,0 +1,8 @@ +using UnityEngine; + +namespace UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; + +public interface INewScriptableObjectTracker +{ + void NewScriptableObject(ScriptableObject scriptableObject); +} \ No newline at end of file From 6e72e61d8e35def2698312be8a15ddcb475b6cae Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 22:01:15 +0100 Subject: [PATCH 044/116] additional ScriptableObject instantiation tracker --- .../Patches/Harmony/ScriptableObjectPatch.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 UniTAS/Patcher/Patches/Harmony/ScriptableObjectPatch.cs diff --git a/UniTAS/Patcher/Patches/Harmony/ScriptableObjectPatch.cs b/UniTAS/Patcher/Patches/Harmony/ScriptableObjectPatch.cs new file mode 100644 index 000000000..b954c166f --- /dev/null +++ b/UniTAS/Patcher/Patches/Harmony/ScriptableObjectPatch.cs @@ -0,0 +1,33 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using HarmonyLib; +using UniTAS.Patcher.Interfaces.Patches.PatchTypes; +using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; +using UniTAS.Patcher.Utils; +using UnityEngine; + +namespace UniTAS.Patcher.Patches.Harmony; + +[RawPatch] +[SuppressMessage("ReSharper", "InconsistentNaming")] +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public class ScriptableObjectPatch +{ + private static readonly INewScriptableObjectTracker NewScriptableObjectTracker = + ContainerStarter.Kernel.GetInstance(); + + [HarmonyPatch] + private class CreateInstanceTracker + { + private static Exception Cleanup(MethodBase original, Exception ex) + { + return PatchHelper.CleanupIgnoreFail(original, ex); + } + + private static void Postfix(ScriptableObject __result) + { + NewScriptableObjectTracker.NewScriptableObject(__result); + } + } +} \ No newline at end of file From e71c3b83590e747bf9023881d0c2cae74985eada Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 22:10:36 +0100 Subject: [PATCH 045/116] fixed dependency collision --- .../UnityFix/SaveScriptableObjectStates.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs index 5f04096e0..a3f3c697d 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs @@ -2,6 +2,7 @@ using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Interfaces.Events.SoftRestart; using UniTAS.Patcher.Interfaces.Events.UnityEvents; +using UniTAS.Patcher.Interfaces.Events.UnityEvents.RunEvenPaused; using UniTAS.Patcher.Models.UnitySafeWrappers.SceneManagement; using UniTAS.Patcher.Services.Logging; using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; @@ -11,7 +12,8 @@ namespace UniTAS.Patcher.Implementations.UnityFix; [Singleton] -public partial class SaveScriptableObjectStates : IOnPreGameRestart, IOnSceneLoad, INewScriptableObjectTracker +public partial class SaveScriptableObjectStates : IOnSceneLoad, INewScriptableObjectTracker, IOnAwakeUnconditional, + IOnPreGameRestart { private readonly List _storedStates = new(); @@ -20,7 +22,16 @@ public partial class SaveScriptableObjectStates : IOnPreGameRestart, IOnSceneLoa public SaveScriptableObjectStates(ILogger logger) { _logger = logger; + } + + private bool _initialized; + + public void AwakeUnconditional() + { + if (_initialized) return; + _initialized = true; + // initial save SaveAll(); } @@ -29,6 +40,7 @@ private void SaveAll() _logger.LogDebug("Saving all ScriptableObject states"); var allScriptableObjects = ResourcesUtils.FindObjectsOfTypeAll(); + _logger.LogDebug($"Found {allScriptableObjects.Length} ScriptableObjects"); foreach (var obj in allScriptableObjects) { From e550e282b9a3a0504bda4b08d255a626d2ff940a Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 22:21:52 +0100 Subject: [PATCH 046/116] destroy instantiated objects rather than storing --- .../UnityFix/SaveScriptableObjectStates.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs index a3f3c697d..58a634930 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs @@ -16,6 +16,7 @@ public partial class SaveScriptableObjectStates : IOnSceneLoad, INewScriptableOb IOnPreGameRestart { private readonly List _storedStates = new(); + private readonly List _destroyObjectsOnRestart = new(); private readonly ILogger _logger; @@ -60,11 +61,19 @@ private void Save(ScriptableObject obj) public void NewScriptableObject(ScriptableObject scriptableObject) { - Save(scriptableObject); + _destroyObjectsOnRestart.Add(scriptableObject); } public void OnPreGameRestart() { + _logger.LogDebug("Destroying all ScriptableObject that was created during runtime"); + foreach (var obj in _destroyObjectsOnRestart) + { + Object.Destroy(obj); + } + + _destroyObjectsOnRestart.Clear(); + _logger.LogDebug("Loading all ScriptableObject states"); foreach (var storedState in _storedStates) From 6b3ba2ef3e735c5aa486b5db811ba20436706242 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 22:23:27 +0100 Subject: [PATCH 047/116] destroy immediate instead of simply destroy --- .../Implementations/UnityFix/SaveScriptableObjectStates.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs index 58a634930..9e2287d33 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs @@ -66,10 +66,11 @@ public void NewScriptableObject(ScriptableObject scriptableObject) public void OnPreGameRestart() { - _logger.LogDebug("Destroying all ScriptableObject that was created during runtime"); + _logger.LogDebug( + $"Destroying all {_destroyObjectsOnRestart.Count} ScriptableObject that was created during runtime"); foreach (var obj in _destroyObjectsOnRestart) { - Object.Destroy(obj); + Object.DestroyImmediate(obj); } _destroyObjectsOnRestart.Clear(); From 5fe51dce1ff440d41ee39e3891c6f72c13d5d278 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 23:07:47 +0100 Subject: [PATCH 048/116] removed operation on scene load --- .../UnityFix/SaveScriptableObjectStates.cs | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs index 9e2287d33..75ebd1bb3 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Interfaces.Events.SoftRestart; -using UniTAS.Patcher.Interfaces.Events.UnityEvents; using UniTAS.Patcher.Interfaces.Events.UnityEvents.RunEvenPaused; -using UniTAS.Patcher.Models.UnitySafeWrappers.SceneManagement; using UniTAS.Patcher.Services.Logging; using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; using UniTAS.Patcher.Utils; @@ -12,8 +10,7 @@ namespace UniTAS.Patcher.Implementations.UnityFix; [Singleton] -public partial class SaveScriptableObjectStates : IOnSceneLoad, INewScriptableObjectTracker, IOnAwakeUnconditional, - IOnPreGameRestart +public partial class SaveScriptableObjectStates : INewScriptableObjectTracker, IOnAwakeUnconditional, IOnPreGameRestart { private readonly List _storedStates = new(); private readonly List _destroyObjectsOnRestart = new(); @@ -83,14 +80,11 @@ public void OnPreGameRestart() } } - public void OnSceneLoad(string sceneName, int sceneBuildIndex, LoadSceneMode loadSceneMode, - LocalPhysicsMode localPhysicsMode) - { - if (loadSceneMode == LoadSceneMode.Single) - { - _storedStates.Clear(); - } - - SaveAll(); - } + // don't think this is required + // testing with 2 scenes and 2 scriptable objects showed that on game start, both scriptable objects are loaded + // public void OnSceneLoad(string sceneName, int sceneBuildIndex, LoadSceneMode loadSceneMode, + // LocalPhysicsMode localPhysicsMode) + // { + // SaveAll(); + // } } \ No newline at end of file From 5b393d0dc4a46b02d1f199bc007938e222d128a6 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 23:23:40 +0100 Subject: [PATCH 049/116] fixed warnings --- UniTAS/Patcher/Implementations/InitManagerGameObject.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/UniTAS/Patcher/Implementations/InitManagerGameObject.cs b/UniTAS/Patcher/Implementations/InitManagerGameObject.cs index 9cb89aa2b..2c7a1ed83 100644 --- a/UniTAS/Patcher/Implementations/InitManagerGameObject.cs +++ b/UniTAS/Patcher/Implementations/InitManagerGameObject.cs @@ -1,4 +1,3 @@ -using System.Diagnostics.CodeAnalysis; using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.MonoBehaviourScripts; using UnityEngine; @@ -8,14 +7,13 @@ namespace UniTAS.Patcher.Implementations; [Register] [ForceInstantiate] [ExcludeRegisterIfTesting] -[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] public class InitManagerGameObject { - public const string GameObjectName = "UniTAS Manager"; + public const string GAME_OBJECT_NAME = "UniTAS Manager"; public InitManagerGameObject() { - var gameObject = new GameObject(GameObjectName); + var gameObject = new GameObject(GAME_OBJECT_NAME); Object.DontDestroyOnLoad(gameObject); // attach scripts From 87894301485eb7803dbc5200b6923bdd637088df Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 1 Oct 2023 23:24:48 +0100 Subject: [PATCH 050/116] added OnDestroy error log --- .../MonoBehaviourScripts/MonoBehaviourUpdateInvoker.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UniTAS/Patcher/MonoBehaviourScripts/MonoBehaviourUpdateInvoker.cs b/UniTAS/Patcher/MonoBehaviourScripts/MonoBehaviourUpdateInvoker.cs index 435369dd9..3ae137701 100644 --- a/UniTAS/Patcher/MonoBehaviourScripts/MonoBehaviourUpdateInvoker.cs +++ b/UniTAS/Patcher/MonoBehaviourScripts/MonoBehaviourUpdateInvoker.cs @@ -1,4 +1,5 @@ using System.Collections; +using UniTAS.Patcher.Services.Logging; using UniTAS.Patcher.Services.UnityEvents; using UniTAS.Patcher.Utils; using UnityEngine; @@ -8,15 +9,22 @@ namespace UniTAS.Patcher.MonoBehaviourScripts; public class MonoBehaviourUpdateInvoker : MonoBehaviour { private IMonoBehEventInvoker _monoBehEventInvoker; + private ILogger _logger; private void Awake() { var kernel = ContainerStarter.Kernel; _monoBehEventInvoker = kernel.GetInstance(); + _logger = kernel.GetInstance(); _monoBehEventInvoker.InvokeAwake(); StartCoroutine(EndOfFrame()); } + private void OnDestroy() + { + _logger.LogError("MonoBehaviourUpdateInvoker destroyed, this should not happen"); + } + private void Start() { _monoBehEventInvoker.InvokeStart(); From a43d79118700adc04d7564aec61e48d4b5358d2a Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 05:09:43 +0100 Subject: [PATCH 051/116] actually target methods to patch --- .../Patcher/Patches/Harmony/ScriptableObjectPatch.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Patches/Harmony/ScriptableObjectPatch.cs b/UniTAS/Patcher/Patches/Harmony/ScriptableObjectPatch.cs index b954c166f..e22f0f8a6 100644 --- a/UniTAS/Patcher/Patches/Harmony/ScriptableObjectPatch.cs +++ b/UniTAS/Patcher/Patches/Harmony/ScriptableObjectPatch.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Reflection; using HarmonyLib; @@ -18,8 +19,16 @@ public class ScriptableObjectPatch ContainerStarter.Kernel.GetInstance(); [HarmonyPatch] - private class CreateInstanceTracker + private class CreateInstanceTrackerString { + private static IEnumerable TargetMethods() + { + yield return AccessTools.Method(typeof(ScriptableObject), nameof(ScriptableObject.CreateInstance), + new[] { typeof(string) }); + yield return AccessTools.Method(typeof(ScriptableObject), nameof(ScriptableObject.CreateInstance), + new[] { typeof(Type) }); + } + private static Exception Cleanup(MethodBase original, Exception ex) { return PatchHelper.CleanupIgnoreFail(original, ex); From d11bb5858752f4cc18f0eb0d30b9c92e3d5bc939 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 05:15:10 +0100 Subject: [PATCH 052/116] fixed const naming change --- UniTAS/Patcher/Patches/Harmony/DontDestroyOnLoadTracker.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Patches/Harmony/DontDestroyOnLoadTracker.cs b/UniTAS/Patcher/Patches/Harmony/DontDestroyOnLoadTracker.cs index 7f02ee460..2d782fccd 100644 --- a/UniTAS/Patcher/Patches/Harmony/DontDestroyOnLoadTracker.cs +++ b/UniTAS/Patcher/Patches/Harmony/DontDestroyOnLoadTracker.cs @@ -28,7 +28,7 @@ private class DontDestroyOnLoadPatch { "BepInEx_Manager", "BepInEx_ThreadingHelper", - InitManagerGameObject.GameObjectName + InitManagerGameObject.GAME_OBJECT_NAME }; private static void Prefix(Object target) From 55ee1bb77bffa76defeed35eaee4b083b6dd2b79 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 20:06:13 +0100 Subject: [PATCH 053/116] fixed self referencing to not recurse forever --- .../Utils/DeepCopyTests.TestTypes.cs | 10 ++ UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs | 17 +++ .../DeepCopyMaxRecursionException.cs | 3 +- UniTAS/Patcher/Utils/DeepCopy.cs | 144 +++++++++++------- 4 files changed, 114 insertions(+), 60 deletions(-) diff --git a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs index e38a9d848..5aa9350f2 100644 --- a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs +++ b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs @@ -53,4 +53,14 @@ private class DictionaryType public Dictionary IntStringDictionary; public Dictionary StringIntDictionary; } + + private class SelfReferencing1 + { + public SelfReferencing2 Nested; + } + + private class SelfReferencing2 + { + public SelfReferencing1 Nested; + } } \ No newline at end of file diff --git a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs index 9e0623509..aa00ea02c 100644 --- a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs +++ b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs @@ -145,4 +145,21 @@ public void DictionaryTypeTest() Assert.Equal(source.IntStringDictionary, result!.IntStringDictionary); Assert.Equal(source.StringIntDictionary, result.StringIntDictionary); } + + [Fact] + public void SelfReferencingTest() + { + var source = new SelfReferencing1(); + var reference2 = new SelfReferencing2(); + source.Nested = reference2; + reference2.Nested = source; + + var result = DeepCopy.MakeDeepCopy(source); + Assert.NotSame(source, result); + Assert.NotSame(source.Nested, result.Nested); + Assert.NotSame(source.Nested.Nested, result); + + Assert.Same(result, result.Nested.Nested); + Assert.Same(result.Nested, result.Nested.Nested.Nested); + } } \ No newline at end of file diff --git a/UniTAS/Patcher/Exceptions/DeepCopyMaxRecursionException.cs b/UniTAS/Patcher/Exceptions/DeepCopyMaxRecursionException.cs index a3a67ae93..5ec00f83d 100644 --- a/UniTAS/Patcher/Exceptions/DeepCopyMaxRecursionException.cs +++ b/UniTAS/Patcher/Exceptions/DeepCopyMaxRecursionException.cs @@ -4,7 +4,8 @@ namespace UniTAS.Patcher.Exceptions; public class DeepCopyMaxRecursionException : Exception { - public DeepCopyMaxRecursionException() : base("MakeDeepCopy recursion depth limit exceeded") + public DeepCopyMaxRecursionException(object source, string path) : base( + $"MakeDeepCopy recursion depth limit exceeded, object type: {source.GetType()}, path: {path}") { } } \ No newline at end of file diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index b2ce9ee86..cbd8bffdd 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using System.Threading; using HarmonyLib; using UniTAS.Patcher.Exceptions; @@ -11,12 +10,12 @@ namespace UniTAS.Patcher.Utils; public static class DeepCopy { - /// - /// A cache for the or similar Add methods for different types. - /// - private static readonly Dictionary AddHandlerCache = new(); - - private static readonly ReaderWriterLock AddHandlerCacheLock = new(); + // /// + // /// A cache for the or similar Add methods for different types. + // /// + // private static readonly Dictionary AddHandlerCache = new(); + // + // private static readonly ReaderWriterLock AddHandlerCacheLock = new(); /// Makes a deep copy of any object /// The type of the instance that should be created; for legacy reasons, this must be a class or interface @@ -53,12 +52,20 @@ public static void MakeDeepCopy(object source, out T result, /// The copy of the original object public static object MakeDeepCopy(object source, Type resultType, Func processor = null, string pathRoot = "") + { + var foundReferences = new List { source }; + return MakeDeepCopy(source, resultType, processor, pathRoot, foundReferences, new List()); + } + + private static object MakeDeepCopy(object source, Type resultType, + Func processor, string pathRoot, IList foundReferences, + IList newReferences) { _makeDeepCopyRecursionDepth++; if (_makeDeepCopyRecursionDepth > MAKE_DEEP_COPY_RECURSION_DEPTH_LIMIT) { _makeDeepCopyRecursionDepth = 0; - throw new DeepCopyMaxRecursionException(); + throw new DeepCopyMaxRecursionException(source, pathRoot); } if (source is null || resultType is null) @@ -82,54 +89,54 @@ public static object MakeDeepCopy(object source, Type resultType, return Enum.ToObject(resultType, (int)source); } - if (type.IsGenericType && resultType.IsGenericType) - { - AddHandlerCacheLock.AcquireReaderLock(200); - try - { - if (!AddHandlerCache.TryGetValue(resultType, out var addInvoker)) - { - var addOperation = AccessTools.FirstMethod(resultType, - m => m.Name == "Add" && m.GetParameters().Length == 1); - if (addOperation is not null) - { - addInvoker = MethodInvoker.GetHandler(addOperation); - } - - _ = AddHandlerCacheLock.UpgradeToWriterLock(200); - AddHandlerCacheLock.AcquireWriterLock(200); - try - { - AddHandlerCache[resultType] = addInvoker; - } - finally - { - AddHandlerCacheLock.ReleaseWriterLock(); - } - } - - if (addInvoker != null) - { - var addableResult = Activator.CreateInstance(resultType); - var newElementType = resultType.GetGenericArguments()[0]; - var i = 0; - foreach (var element in (IEnumerable)source) - { - var iStr = i++.ToString(); - var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; - var newElement = MakeDeepCopy(element, newElementType, processor, path); - _ = addInvoker(addableResult, newElement); - } - - _makeDeepCopyRecursionDepth--; - return addableResult; - } - } - finally - { - AddHandlerCacheLock.ReleaseReaderLock(); - } - } + // if (type.IsGenericType && resultType.IsGenericType) + // { + // AddHandlerCacheLock.AcquireReaderLock(200); + // try + // { + // if (!AddHandlerCache.TryGetValue(resultType, out var addInvoker)) + // { + // var addOperation = AccessTools.FirstMethod(resultType, + // m => m.Name == "Add" && m.GetParameters().Length == 1); + // if (addOperation is not null) + // { + // addInvoker = MethodInvoker.GetHandler(addOperation); + // } + // + // _ = AddHandlerCacheLock.UpgradeToWriterLock(200); + // AddHandlerCacheLock.AcquireWriterLock(200); + // try + // { + // AddHandlerCache[resultType] = addInvoker; + // } + // finally + // { + // AddHandlerCacheLock.ReleaseWriterLock(); + // } + // } + // + // if (addInvoker != null) + // { + // var addableResult = Activator.CreateInstance(resultType); + // var newElementType = resultType.GetGenericArguments()[0]; + // var i = 0; + // foreach (var element in (IEnumerable)source) + // { + // var iStr = i++.ToString(); + // var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; + // var newElement = MakeDeepCopy(element, newElementType, processor, path, foundReferences, newReferences); + // _ = addInvoker(addableResult, newElement); + // } + // + // _makeDeepCopyRecursionDepth--; + // return addableResult; + // } + // } + // finally + // { + // AddHandlerCacheLock.ReleaseReaderLock(); + // } + // } if (type.IsArray && resultType.IsArray) { @@ -140,11 +147,14 @@ public static object MakeDeepCopy(object source, Type resultType, { var iStr = i.ToString(); var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; - var newElement = MakeDeepCopy(array.GetValue(i), newElementType, processor, path); + var newElement = MakeDeepCopy(array.GetValue(i), newElementType, processor, path, foundReferences, + newReferences); newArray.SetValue(newElement, i); } _makeDeepCopyRecursionDepth--; + foundReferences.Add(source); + newReferences.Add(newArray); return newArray; } @@ -174,13 +184,16 @@ public static object MakeDeepCopy(object source, Type resultType, (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(resultTypeGenericArgument)); foreach (var element in sourceCollection) { - var newElement = MakeDeepCopy(element, resultTypeGenericArgument, processor, pathRoot); + var newElement = MakeDeepCopy(element, resultTypeGenericArgument, processor, pathRoot, + foundReferences, newReferences); tempResultList.Add(newElement); } var addableResult = ctor.Invoke(new object[] { tempResultList }); _makeDeepCopyRecursionDepth--; + foundReferences.Add(source); + newReferences.Add(addableResult); return addableResult; } } @@ -193,6 +206,7 @@ public static object MakeDeepCopy(object source, Type resultType, } var result = AccessTools.CreateInstance(resultType == typeof(object) ? type : resultType); + newReferences.Add(result); Traverse.IterateFields(source, result, (name, src, dst) => { // stupid hack to get FieldInfo from Traverse @@ -209,7 +223,19 @@ public static object MakeDeepCopy(object source, Type resultType, var path = pathRoot.Length > 0 ? pathRoot + "." + name : name; var value = processor is not null ? processor(path, src, dst) : src.GetValue(); - dst.SetValue(MakeDeepCopy(value, dst.GetValueType(), processor, path)); + + // check reference stuff + var foundReferenceIndex = foundReferences.IndexOf(value); + if (foundReferenceIndex >= 0 && foundReferences.Count <= newReferences.Count) + { + var newReference = newReferences[foundReferenceIndex]; + dst.SetValue(newReference); + return; + } + + var copiedObj = MakeDeepCopy(value, dst.GetValueType(), processor, path, foundReferences, newReferences); + newReferences.Add(value); + dst.SetValue(copiedObj); }); _makeDeepCopyRecursionDepth--; return result; From 0b2f6a840156b628c92d2a03f51d367ef1294e9e Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 20:57:14 +0100 Subject: [PATCH 054/116] additional deep copy test --- .../Utils/DeepCopyTests.TestTypes.cs | 12 +++++++++++- UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs index 5aa9350f2..cb1f7f549 100644 --- a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs +++ b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs @@ -16,7 +16,7 @@ private class SimpleType public static bool ThisShouldBeIgnored; [SuppressMessage("ReSharper", "UnusedMember.Local")] - private const int ThisShouldBeIgnoredToo = 42; + private const int THIS_SHOULD_BE_IGNORED_TOO = 42; } private enum TestEnum @@ -63,4 +63,14 @@ private class SelfReferencing2 { public SelfReferencing1 Nested; } + + private class SelfReferencing3 + { + public SelfReferencing4 Nested; + } + + private class SelfReferencing4 + { + public List Nested; + } } \ No newline at end of file diff --git a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs index aa00ea02c..6ddfb8414 100644 --- a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs +++ b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Linq; using UniTAS.Patcher.Utils; namespace Patcher.Tests.Utils; @@ -162,4 +163,21 @@ public void SelfReferencingTest() Assert.Same(result, result.Nested.Nested); Assert.Same(result.Nested, result.Nested.Nested.Nested); } + + [Fact] + public void SelfReferencingTest2() + { + var source = new SelfReferencing3(); + var reference2 = new SelfReferencing4(); + source.Nested = reference2; + reference2.Nested = new() { source }; + + var result = DeepCopy.MakeDeepCopy(source); + Assert.NotSame(source, result); + Assert.NotSame(source.Nested, result.Nested); + Assert.NotSame(source.Nested.Nested.FirstOrDefault(), result); + + Assert.Same(result, result.Nested.Nested.FirstOrDefault()); + Assert.Same(result.Nested, result.Nested.Nested.FirstOrDefault()?.Nested); + } } \ No newline at end of file From 3b8020f66f5b27db5f4a451060559ed46cdb964c Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 20:57:41 +0100 Subject: [PATCH 055/116] use of reference cache dictionaries --- UniTAS/Patcher/Utils/DeepCopy.cs | 141 +++++++++++-------------------- 1 file changed, 50 insertions(+), 91 deletions(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index cbd8bffdd..e2ce85739 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -2,7 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; -using System.Reflection; using HarmonyLib; using UniTAS.Patcher.Exceptions; @@ -10,18 +9,10 @@ namespace UniTAS.Patcher.Utils; public static class DeepCopy { - // /// - // /// A cache for the or similar Add methods for different types. - // /// - // private static readonly Dictionary AddHandlerCache = new(); - // - // private static readonly ReaderWriterLock AddHandlerCacheLock = new(); - /// Makes a deep copy of any object /// The type of the instance that should be created; for legacy reasons, this must be a class or interface /// The original object /// A copy of the original object but of type T - /// public static T MakeDeepCopy(object source) where T : class { return MakeDeepCopy(source, typeof(T)) as T; @@ -53,13 +44,15 @@ public static void MakeDeepCopy(object source, out T result, public static object MakeDeepCopy(object source, Type resultType, Func processor = null, string pathRoot = "") { - var foundReferences = new List { source }; - return MakeDeepCopy(source, resultType, processor, pathRoot, foundReferences, new List()); + var foundReferences = new Dictionary { { 0, source } }; + var id = 1ul; + return MakeDeepCopy(source, resultType, processor, pathRoot, foundReferences, new(), ref id); } + // dictionary is used to keep track of instances. int is the ID of the instance, which is used to compare foundReferences and newReferences private static object MakeDeepCopy(object source, Type resultType, - Func processor, string pathRoot, IList foundReferences, - IList newReferences) + Func processor, string pathRoot, Dictionary foundReferences, + Dictionary newReferences, ref ulong id) { _makeDeepCopyRecursionDepth++; if (_makeDeepCopyRecursionDepth > MAKE_DEEP_COPY_RECURSION_DEPTH_LIMIT) @@ -74,6 +67,16 @@ private static object MakeDeepCopy(object source, Type resultType, return null; } + if (foundReferences.ContainsValue(source)) + { + var foundId = foundReferences.First(x => ReferenceEquals(x.Value, source)).Key; + if (newReferences.TryGetValue(foundId, out var newReference)) + { + _makeDeepCopyRecursionDepth--; + return newReference; + } + } + resultType = Nullable.GetUnderlyingType(resultType) ?? resultType; var type = source.GetType(); @@ -89,72 +92,25 @@ private static object MakeDeepCopy(object source, Type resultType, return Enum.ToObject(resultType, (int)source); } - // if (type.IsGenericType && resultType.IsGenericType) - // { - // AddHandlerCacheLock.AcquireReaderLock(200); - // try - // { - // if (!AddHandlerCache.TryGetValue(resultType, out var addInvoker)) - // { - // var addOperation = AccessTools.FirstMethod(resultType, - // m => m.Name == "Add" && m.GetParameters().Length == 1); - // if (addOperation is not null) - // { - // addInvoker = MethodInvoker.GetHandler(addOperation); - // } - // - // _ = AddHandlerCacheLock.UpgradeToWriterLock(200); - // AddHandlerCacheLock.AcquireWriterLock(200); - // try - // { - // AddHandlerCache[resultType] = addInvoker; - // } - // finally - // { - // AddHandlerCacheLock.ReleaseWriterLock(); - // } - // } - // - // if (addInvoker != null) - // { - // var addableResult = Activator.CreateInstance(resultType); - // var newElementType = resultType.GetGenericArguments()[0]; - // var i = 0; - // foreach (var element in (IEnumerable)source) - // { - // var iStr = i++.ToString(); - // var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; - // var newElement = MakeDeepCopy(element, newElementType, processor, path, foundReferences, newReferences); - // _ = addInvoker(addableResult, newElement); - // } - // - // _makeDeepCopyRecursionDepth--; - // return addableResult; - // } - // } - // finally - // { - // AddHandlerCacheLock.ReleaseReaderLock(); - // } - // } - if (type.IsArray && resultType.IsArray) { var newElementType = resultType.GetElementType(); var array = (Array)source; + foundReferences.Add(id, source); + var newRefId = id; + id++; var newArray = Array.CreateInstance(newElementType ?? throw new InvalidOperationException(), array.Length); for (var i = 0; i < array.Length; i++) { var iStr = i.ToString(); var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; var newElement = MakeDeepCopy(array.GetValue(i), newElementType, processor, path, foundReferences, - newReferences); + newReferences, ref id); newArray.SetValue(newElement, i); } _makeDeepCopyRecursionDepth--; - foundReferences.Add(source); - newReferences.Add(newArray); + newReferences.Add(newRefId, newArray); return newArray; } @@ -162,6 +118,9 @@ private static object MakeDeepCopy(object source, Type resultType, if (typeof(IEnumerable).IsAssignableFrom(type) && typeof(IEnumerable).IsAssignableFrom(resultType)) { var sourceCollection = (IEnumerable)source; + foundReferences.Add(id, source); + var newRefId = id; + id++; Type resultTypeInterface; if (resultType.IsInterface) @@ -185,15 +144,14 @@ private static object MakeDeepCopy(object source, Type resultType, foreach (var element in sourceCollection) { var newElement = MakeDeepCopy(element, resultTypeGenericArgument, processor, pathRoot, - foundReferences, newReferences); + foundReferences, newReferences, ref id); tempResultList.Add(newElement); } var addableResult = ctor.Invoke(new object[] { tempResultList }); _makeDeepCopyRecursionDepth--; - foundReferences.Add(source); - newReferences.Add(addableResult); + newReferences.Add(newRefId, addableResult); return addableResult; } } @@ -205,38 +163,39 @@ private static object MakeDeepCopy(object source, Type resultType, return source; } + // guaranteed to be a reference type var result = AccessTools.CreateInstance(resultType == typeof(object) ? type : resultType); - newReferences.Add(result); - Traverse.IterateFields(source, result, (name, src, dst) => + foundReferences.Add(id, source); + newReferences.Add(id, result); + id++; + + var fields = AccessTools.GetDeclaredFields(type); + + foreach (var field in fields) { - // stupid hack to get FieldInfo from Traverse - var srcField = Traverse.Create(src).Field("_info").GetValue(); - if (srcField is null) + if (field.IsStatic || field.IsLiteral) continue; + + var name = field.Name; + var path = pathRoot.Length > 0 ? pathRoot + "." + name : name; + object value; + if (processor is not null) { - throw new NullReferenceException("srcField is null, this should never happen"); - } + var srcTraverse = Traverse.Create(source).Field(name); + var dstTraverse = Traverse.Create(result).Field(name); - if (srcField.IsStatic || srcField.IsLiteral) + value = processor(path, srcTraverse, dstTraverse); + } + else { - return; + value = field.GetValue(source); } - var path = pathRoot.Length > 0 ? pathRoot + "." + name : name; - var value = processor is not null ? processor(path, src, dst) : src.GetValue(); + var copiedObj = MakeDeepCopy(value, field.FieldType, processor, path, foundReferences, newReferences, + ref id); - // check reference stuff - var foundReferenceIndex = foundReferences.IndexOf(value); - if (foundReferenceIndex >= 0 && foundReferences.Count <= newReferences.Count) - { - var newReference = newReferences[foundReferenceIndex]; - dst.SetValue(newReference); - return; - } + field.SetValue(result, copiedObj); + } - var copiedObj = MakeDeepCopy(value, dst.GetValueType(), processor, path, foundReferences, newReferences); - newReferences.Add(value); - dst.SetValue(copiedObj); - }); _makeDeepCopyRecursionDepth--; return result; } From b575bd7f63723a8c1010985c873761fe5b6d3443 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 20:59:52 +0100 Subject: [PATCH 056/116] fixed found references to not include copy source --- UniTAS/Patcher/Utils/DeepCopy.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index e2ce85739..0b602441a 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -44,9 +44,8 @@ public static void MakeDeepCopy(object source, out T result, public static object MakeDeepCopy(object source, Type resultType, Func processor = null, string pathRoot = "") { - var foundReferences = new Dictionary { { 0, source } }; - var id = 1ul; - return MakeDeepCopy(source, resultType, processor, pathRoot, foundReferences, new(), ref id); + var id = 0ul; + return MakeDeepCopy(source, resultType, processor, pathRoot, new(), new(), ref id); } // dictionary is used to keep track of instances. int is the ID of the instance, which is used to compare foundReferences and newReferences From 1c177b43296a6973207c19a377e69436e1616fb2 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 21:11:37 +0100 Subject: [PATCH 057/116] fixed reference finding to uses ReferenceEquals --- UniTAS/Patcher/Utils/DeepCopy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 0b602441a..86f23489e 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -66,7 +66,7 @@ private static object MakeDeepCopy(object source, Type resultType, return null; } - if (foundReferences.ContainsValue(source)) + if (foundReferences.Any(x => ReferenceEquals(x.Value, source))) { var foundId = foundReferences.First(x => ReferenceEquals(x.Value, source)).Key; if (newReferences.TryGetValue(foundId, out var newReference)) From 5b9b2ae9b98a04d8cbce1de3200fe3d306635ad1 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 21:16:17 +0100 Subject: [PATCH 058/116] fix reference adding order --- UniTAS/Patcher/Utils/DeepCopy.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 86f23489e..d60c8ed03 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -95,10 +95,10 @@ private static object MakeDeepCopy(object source, Type resultType, { var newElementType = resultType.GetElementType(); var array = (Array)source; + var newArray = Array.CreateInstance(newElementType ?? throw new InvalidOperationException(), array.Length); foundReferences.Add(id, source); - var newRefId = id; + newReferences.Add(id, newArray); id++; - var newArray = Array.CreateInstance(newElementType ?? throw new InvalidOperationException(), array.Length); for (var i = 0; i < array.Length; i++) { var iStr = i.ToString(); @@ -109,7 +109,6 @@ private static object MakeDeepCopy(object source, Type resultType, } _makeDeepCopyRecursionDepth--; - newReferences.Add(newRefId, newArray); return newArray; } From 8d9ea5d66b3c28628130428f1ef12b6af26c69fd Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 21:31:56 +0100 Subject: [PATCH 059/116] fixed edge cases throwing --- UniTAS/Patcher/Utils/DeepCopy.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index d60c8ed03..6584955ac 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -161,8 +161,9 @@ private static object MakeDeepCopy(object source, Type resultType, return source; } + var result = + AccessTools.CreateInstance(resultType == typeof(object) || resultType.IsAbstract ? type : resultType); // guaranteed to be a reference type - var result = AccessTools.CreateInstance(resultType == typeof(object) ? type : resultType); foundReferences.Add(id, source); newReferences.Add(id, result); id++; @@ -188,7 +189,7 @@ private static object MakeDeepCopy(object source, Type resultType, value = field.GetValue(source); } - var copiedObj = MakeDeepCopy(value, field.FieldType, processor, path, foundReferences, newReferences, + var copiedObj = MakeDeepCopy(value, value.GetType(), processor, path, foundReferences, newReferences, ref id); field.SetValue(result, copiedObj); From 44ed1944e7f8c2c7f71207ad8acbf057205028cb Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 23:00:01 +0100 Subject: [PATCH 060/116] additional logging just in case --- .../UnityFix/SaveScriptableObjectStates.Types.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs index d443c61a4..0700989aa 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using System.Reflection; using HarmonyLib; @@ -5,6 +6,7 @@ using UniTAS.Patcher.Services.Logging; using UniTAS.Patcher.Utils; using UnityEngine; +using Object = UnityEngine.Object; namespace UniTAS.Patcher.Implementations.UnityFix; @@ -27,7 +29,7 @@ public StoredState(ScriptableObject scriptableObject, ILogger logger) // TODO limit fields that are serializable to save space var fields = AccessTools.GetDeclaredFields(scriptableObject.GetType()) .Where(x => !x.IsStatic && !x.IsLiteral); - _savedFields = fields.Select(x => new FieldData(x, scriptableObject)).ToArray(); + _savedFields = fields.Select(x => new FieldData(x, scriptableObject, logger)).ToArray(); } public void Load() @@ -51,7 +53,7 @@ private readonly struct FieldData private readonly Either _value; private readonly FieldInfo _saveField; - public FieldData(FieldInfo fieldInfo, ScriptableObject instance) + public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger) { _saveField = fieldInfo; _instance = instance; @@ -63,7 +65,14 @@ public FieldData(FieldInfo fieldInfo, ScriptableObject instance) } else { - _value = DeepCopy.MakeDeepCopy(value, value.GetType()); + try + { + _value = DeepCopy.MakeDeepCopy(value); + } + catch (Exception e) + { + logger.LogError($"Failed to deep copy field value, type is {fieldInfo.FieldType.FullName}, {e}"); + } } } From ec50485852b0676a2850d03a23dcd7b311f97ba8 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 23:00:14 +0100 Subject: [PATCH 061/116] addition of unsafe field test --- UniTAS/Patcher.Tests/Patcher.Tests.csproj | 2 ++ .../Utils/DeepCopyTests.TestTypes.cs | 5 ++++ UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs | 23 ++++++++++++++++++- 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher.Tests/Patcher.Tests.csproj b/UniTAS/Patcher.Tests/Patcher.Tests.csproj index 9f8d3810a..3b53f634b 100644 --- a/UniTAS/Patcher.Tests/Patcher.Tests.csproj +++ b/UniTAS/Patcher.Tests/Patcher.Tests.csproj @@ -9,6 +9,8 @@ false + + true diff --git a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs index cb1f7f549..f8bd05880 100644 --- a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs +++ b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.TestTypes.cs @@ -73,4 +73,9 @@ private class SelfReferencing4 { public List Nested; } + + private class PointerType + { + public unsafe void* Pointer; + } } \ No newline at end of file diff --git a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs index 6ddfb8414..2e35587d6 100644 --- a/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs +++ b/UniTAS/Patcher.Tests/Utils/DeepCopyTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; using UniTAS.Patcher.Utils; @@ -180,4 +181,24 @@ public void SelfReferencingTest2() Assert.Same(result, result.Nested.Nested.FirstOrDefault()); Assert.Same(result.Nested, result.Nested.Nested.FirstOrDefault()?.Nested); } + + [Fact] + public unsafe void PointerTypeTest() + { + var source = new PointerType + { + Pointer = (void*)0x12345678, + }; + var result = DeepCopy.MakeDeepCopy(source); + + Assert.Equal((IntPtr)source.Pointer, (IntPtr)result.Pointer); + + result.Pointer = (void*)0x87654321; + + Assert.NotEqual((IntPtr)source.Pointer, (IntPtr)result.Pointer); + + DeepCopy.MakeDeepCopy(source, out result); + + Assert.Equal((IntPtr)source.Pointer, (IntPtr)result!.Pointer); + } } \ No newline at end of file From ee54f251e684ac2784afb4f1ed3967f60b3ea0e8 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 23:00:43 +0100 Subject: [PATCH 062/116] fixed handling unsafe fields --- UniTAS/Patcher/Utils/DeepCopy.cs | 68 ++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 25 deletions(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 6584955ac..ffd3bbf45 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Reflection; using HarmonyLib; using UniTAS.Patcher.Exceptions; @@ -15,7 +16,7 @@ public static class DeepCopy /// A copy of the original object but of type T public static T MakeDeepCopy(object source) where T : class { - return MakeDeepCopy(source, typeof(T)) as T; + return MakeDeepCopy(source) as T; } /// Makes a deep copy of any object @@ -28,7 +29,7 @@ public static T MakeDeepCopy(object source) where T : class public static void MakeDeepCopy(object source, out T result, Func processor = null, string pathRoot = "") { - result = (T)MakeDeepCopy(source, typeof(T), processor, pathRoot); + result = (T)MakeDeepCopy(source, processor, pathRoot); } private static int _makeDeepCopyRecursionDepth; @@ -37,20 +38,19 @@ public static void MakeDeepCopy(object source, out T result, /// Makes a deep copy of any object /// The original object - /// The type of the instance that should be created /// Optional value transformation function (taking a field name and src/dst instances) /// The optional path root to start with /// The copy of the original object - public static object MakeDeepCopy(object source, Type resultType, - Func processor = null, string pathRoot = "") + public static object MakeDeepCopy(object source, Func processor = null, + string pathRoot = "") { var id = 0ul; - return MakeDeepCopy(source, resultType, processor, pathRoot, new(), new(), ref id); + return MakeDeepCopy(source, processor, pathRoot, new(), new(), ref id); } // dictionary is used to keep track of instances. int is the ID of the instance, which is used to compare foundReferences and newReferences - private static object MakeDeepCopy(object source, Type resultType, - Func processor, string pathRoot, Dictionary foundReferences, + private static object MakeDeepCopy(object source, Func processor, + string pathRoot, Dictionary foundReferences, Dictionary newReferences, ref ulong id) { _makeDeepCopyRecursionDepth++; @@ -60,12 +60,14 @@ private static object MakeDeepCopy(object source, Type resultType, throw new DeepCopyMaxRecursionException(source, pathRoot); } - if (source is null || resultType is null) + if (source is null) { _makeDeepCopyRecursionDepth--; return null; } + StaticLogger.Log.LogDebug($"making deep copy of {source.GetType().FullName}, pathRoot: {pathRoot}"); + if (foundReferences.Any(x => ReferenceEquals(x.Value, source))) { var foundId = foundReferences.First(x => ReferenceEquals(x.Value, source)).Key; @@ -76,7 +78,6 @@ private static object MakeDeepCopy(object source, Type resultType, } } - resultType = Nullable.GetUnderlyingType(resultType) ?? resultType; var type = source.GetType(); if (type.IsPrimitive) @@ -88,12 +89,12 @@ private static object MakeDeepCopy(object source, Type resultType, if (type.IsEnum) { _makeDeepCopyRecursionDepth--; - return Enum.ToObject(resultType, (int)source); + return Enum.ToObject(type, (int)source); } - if (type.IsArray && resultType.IsArray) + if (type.IsArray) { - var newElementType = resultType.GetElementType(); + var newElementType = type.GetElementType(); var array = (Array)source; var newArray = Array.CreateInstance(newElementType ?? throw new InvalidOperationException(), array.Length); foundReferences.Add(id, source); @@ -103,8 +104,8 @@ private static object MakeDeepCopy(object source, Type resultType, { var iStr = i.ToString(); var path = pathRoot.Length > 0 ? pathRoot + "." + iStr : iStr; - var newElement = MakeDeepCopy(array.GetValue(i), newElementType, processor, path, foundReferences, - newReferences, ref id); + var newElement = MakeDeepCopy(array.GetValue(i), processor, path, foundReferences, newReferences, + ref id); newArray.SetValue(newElement, i); } @@ -113,7 +114,7 @@ private static object MakeDeepCopy(object source, Type resultType, } // is type a collection? - if (typeof(IEnumerable).IsAssignableFrom(type) && typeof(IEnumerable).IsAssignableFrom(resultType)) + if (typeof(IEnumerable).IsAssignableFrom(type)) { var sourceCollection = (IEnumerable)source; foundReferences.Add(id, source); @@ -121,27 +122,27 @@ private static object MakeDeepCopy(object source, Type resultType, id++; Type resultTypeInterface; - if (resultType.IsInterface) + if (type.IsInterface) { - resultTypeInterface = resultType; + resultTypeInterface = type; } else { - resultTypeInterface = resultType.GetInterfaces().First(i => + resultTypeInterface = type.GetInterfaces().First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); } var resultTypeGenericArgument = resultTypeInterface.GetGenericArguments()[0]; var iEnumerableType = typeof(IEnumerable<>).MakeGenericType(resultTypeGenericArgument); - var ctor = AccessTools.Constructor(resultType, new[] { iEnumerableType }); + var ctor = AccessTools.Constructor(type, new[] { iEnumerableType }); if (ctor != null) { var tempResultList = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(resultTypeGenericArgument)); foreach (var element in sourceCollection) { - var newElement = MakeDeepCopy(element, resultTypeGenericArgument, processor, pathRoot, + var newElement = MakeDeepCopy(element, processor, pathRoot, foundReferences, newReferences, ref id); tempResultList.Add(newElement); } @@ -161,19 +162,24 @@ private static object MakeDeepCopy(object source, Type resultType, return source; } - var result = - AccessTools.CreateInstance(resultType == typeof(object) || resultType.IsAbstract ? type : resultType); + if (type == typeof(void*)) + + StaticLogger.Log.LogDebug("creating new instance"); + var result = AccessTools.CreateInstance(type); // guaranteed to be a reference type foundReferences.Add(id, source); newReferences.Add(id, result); id++; + StaticLogger.Log.LogDebug("getting fields"); var fields = AccessTools.GetDeclaredFields(type); foreach (var field in fields) { if (field.IsStatic || field.IsLiteral) continue; + StaticLogger.Log.LogDebug($"field is {field.Name}"); + var name = field.Name; var path = pathRoot.Length > 0 ? pathRoot + "." + name : name; object value; @@ -189,8 +195,20 @@ private static object MakeDeepCopy(object source, Type resultType, value = field.GetValue(source); } - var copiedObj = MakeDeepCopy(value, value.GetType(), processor, path, foundReferences, newReferences, - ref id); + if (field.FieldType.IsPointer) + { + StaticLogger.Log.LogDebug("its a pointer field"); + unsafe + { + field.SetValue(result, (IntPtr)Pointer.Unbox(value)); + } + + continue; + } + + StaticLogger.Log.LogDebug($"value is {value?.GetType().FullName}, field is {field.FieldType.FullName}"); + var copiedObj = MakeDeepCopy(value, processor, path, foundReferences, newReferences, ref id); + StaticLogger.Log.LogDebug($"copied object type is {copiedObj?.GetType().FullName}"); field.SetValue(result, copiedObj); } From f10ca43f04a3e4738627062b6b7095fdd1011c40 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 23:02:31 +0100 Subject: [PATCH 063/116] removal of logs --- UniTAS/Patcher/Utils/DeepCopy.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index ffd3bbf45..00487b393 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -66,8 +66,6 @@ private static object MakeDeepCopy(object source, Func ReferenceEquals(x.Value, source))) { var foundId = foundReferences.First(x => ReferenceEquals(x.Value, source)).Key; @@ -162,24 +160,18 @@ private static object MakeDeepCopy(object source, Func 0 ? pathRoot + "." + name : name; object value; @@ -197,7 +189,6 @@ private static object MakeDeepCopy(object source, Func Date: Mon, 2 Oct 2023 23:49:20 +0100 Subject: [PATCH 064/116] malloc tracker and free service --- .../Patcher/Implementations/MallocTracker.cs | 93 ++++++++++++++++ .../Harmony/UnityUnsafeUtilityPatch.cs | 103 ++++++++++++++++++ UniTAS/Patcher/Services/ITryFreeMalloc.cs | 11 ++ .../UpdateTrackInfo/IUnityMallocTracker.cs | 10 ++ 4 files changed, 217 insertions(+) create mode 100644 UniTAS/Patcher/Implementations/MallocTracker.cs create mode 100644 UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs create mode 100644 UniTAS/Patcher/Services/ITryFreeMalloc.cs create mode 100644 UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IUnityMallocTracker.cs diff --git a/UniTAS/Patcher/Implementations/MallocTracker.cs b/UniTAS/Patcher/Implementations/MallocTracker.cs new file mode 100644 index 000000000..3bdc920dd --- /dev/null +++ b/UniTAS/Patcher/Implementations/MallocTracker.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using HarmonyLib; +using UniTAS.Patcher.Interfaces.DependencyInjection; +using UniTAS.Patcher.Models.UnitySafeWrappers.Unity.Collections; +using UniTAS.Patcher.Services; +using UniTAS.Patcher.Services.Logging; +using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; + +namespace UniTAS.Patcher.Implementations; + +[Singleton] +public class MallocTracker : ITryFreeMalloc, IUnityMallocTracker +{ + private readonly List _unityMallocs = new(); + + private readonly MethodBase _unityFree = AccessTools.Method("Unity.Collections.LowLevel.Unsafe.UnsafeUtility:Free"); + + private readonly MethodBase _unityFreeTracked = + AccessTools.Method("Unity.Collections.LowLevel.Unsafe.UnsafeUtility:FreeTracked"); + + private readonly Type _allocator = AccessTools.TypeByName("Unity.Collections.Allocator"); + + private readonly bool _hasUnityMalloc; + + private readonly ILogger _logger; + + public MallocTracker(ILogger logger) + { + _logger = logger; + + _hasUnityMalloc = _unityFree != null && _unityFreeTracked != null && _allocator != null; + } + + public void TryFree(IntPtr ptr) + { + if (_hasUnityMalloc && TryFreeUnityMalloc(ptr)) return; + + _logger.LogWarning($"Could not find malloc at {ptr} to free"); + } + + private bool TryFreeUnityMalloc(IntPtr ptr) + { + var unityMallocIndex = _unityMallocs.FindIndex(x => x.Ptr == ptr); + if (unityMallocIndex < 0) return false; + + var unityMalloc = _unityMallocs[unityMallocIndex]; + + var allocator = Enum.Parse(_allocator, unityMalloc.Allocator.ToString()); + _logger.LogDebug($"Freeing unity malloc at {ptr} with allocator {allocator}"); + + // free + if (unityMalloc.Tracked) + { + _unityFreeTracked.Invoke(null, new[] { ptr, allocator }); + } + else + { + _unityFree.Invoke(null, new[] { ptr, allocator }); + } + + return true; + } + + public void Malloc(IntPtr ptr, Allocator allocator, bool tracked) + { + _unityMallocs.Add(new(ptr, allocator, tracked)); + } + + public void Free(IntPtr ptr, Allocator allocator, bool tracked) + { + var mallocIndex = + _unityMallocs.FindIndex(x => x.Ptr == ptr && x.Allocator == allocator && x.Tracked == tracked); + if (mallocIndex < 0) return; + + _unityMallocs.RemoveAt(mallocIndex); + } + + private readonly struct UnityMallocInfo + { + public readonly IntPtr Ptr; + public readonly Allocator Allocator; + public readonly bool Tracked; + + public UnityMallocInfo(IntPtr ptr, Allocator allocator, bool tracked) + { + Ptr = ptr; + Allocator = allocator; + Tracked = tracked; + } + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs b/UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs new file mode 100644 index 000000000..e4de39bd1 --- /dev/null +++ b/UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs @@ -0,0 +1,103 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using HarmonyLib; +using UniTAS.Patcher.Interfaces.Patches.PatchTypes; +using UniTAS.Patcher.Models.UnitySafeWrappers.Unity.Collections; +using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; +using UniTAS.Patcher.Utils; + +namespace UniTAS.Patcher.Patches.Harmony; + +[RawPatch] +[SuppressMessage("ReSharper", "UnusedMember.Local")] +[SuppressMessage("ReSharper", "InconsistentNaming")] +public class UnityUnsafeUtilityPatch +{ + private static readonly IUnityMallocTracker UnityMallocTracker = + ContainerStarter.Kernel.GetInstance(); + + [HarmonyPatch] + private class Malloc + { + private static Exception Cleanup(MethodBase original, Exception ex) + { + return PatchHelper.CleanupIgnoreFail(original, ex); + } + + private static MethodBase TargetMethod() + { + return AccessTools.Method("Unity.Collections.LowLevel.Unsafe.UnsafeUtility:Malloc"); + } + + private static unsafe void Postfix(void* __result, object allocator) + { + StaticLogger.Log.LogDebug($"Malloc, with allocator: {allocator}"); + var allocatorTranslated = (Allocator)Enum.Parse(typeof(Allocator), allocator.ToString()); + UnityMallocTracker.Malloc((IntPtr)__result, allocatorTranslated, false); + } + } + + [HarmonyPatch] + private class MallocTracked + { + private static Exception Cleanup(MethodBase original, Exception ex) + { + return PatchHelper.CleanupIgnoreFail(original, ex); + } + + private static MethodBase TargetMethod() + { + return AccessTools.Method("Unity.Collections.LowLevel.Unsafe.UnsafeUtility:MallocTracked"); + } + + private static unsafe void Postfix(void* __result, object allocator) + { + StaticLogger.Log.LogDebug($"Malloc tracked, with allocator: {allocator}"); + var allocatorTranslated = (Allocator)Enum.Parse(typeof(Allocator), allocator.ToString()); + UnityMallocTracker.Malloc((IntPtr)__result, allocatorTranslated, true); + } + } + + [HarmonyPatch] + private class Free + { + private static Exception Cleanup(MethodBase original, Exception ex) + { + return PatchHelper.CleanupIgnoreFail(original, ex); + } + + private static MethodBase TargetMethod() + { + return AccessTools.Method("Unity.Collections.LowLevel.Unsafe.UnsafeUtility:Free"); + } + + private static unsafe void Prefix(void* memory, object allocator) + { + StaticLogger.Log.LogDebug($"Free with allocator: {allocator}"); + var allocatorTranslated = (Allocator)Enum.Parse(typeof(Allocator), allocator.ToString()); + UnityMallocTracker.Free((IntPtr)memory, allocatorTranslated, false); + } + } + + [HarmonyPatch] + private class FreeTracked + { + private static Exception Cleanup(MethodBase original, Exception ex) + { + return PatchHelper.CleanupIgnoreFail(original, ex); + } + + private static MethodBase TargetMethod() + { + return AccessTools.Method("Unity.Collections.LowLevel.Unsafe.UnsafeUtility:FreeTracked"); + } + + private static unsafe void Prefix(void* memory, object allocator) + { + StaticLogger.Log.LogDebug($"FreeTracked with allocator: {allocator}"); + var allocatorTranslated = (Allocator)Enum.Parse(typeof(Allocator), allocator.ToString()); + UnityMallocTracker.Free((IntPtr)memory, allocatorTranslated, true); + } + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Services/ITryFreeMalloc.cs b/UniTAS/Patcher/Services/ITryFreeMalloc.cs new file mode 100644 index 000000000..81c978936 --- /dev/null +++ b/UniTAS/Patcher/Services/ITryFreeMalloc.cs @@ -0,0 +1,11 @@ +using System; + +namespace UniTAS.Patcher.Services; + +public interface ITryFreeMalloc +{ + /// + /// Tries to free the memory at the given pointer + /// + void TryFree(IntPtr ptr); +} \ No newline at end of file diff --git a/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IUnityMallocTracker.cs b/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IUnityMallocTracker.cs new file mode 100644 index 000000000..a88e788d6 --- /dev/null +++ b/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IUnityMallocTracker.cs @@ -0,0 +1,10 @@ +using System; +using UniTAS.Patcher.Models.UnitySafeWrappers.Unity.Collections; + +namespace UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; + +public interface IUnityMallocTracker +{ + void Malloc(IntPtr ptr, Allocator allocator, bool tracked); + void Free(IntPtr ptr, Allocator allocator, bool tracked); +} \ No newline at end of file From bbe31e6f6c3ede2ce9005f1ea163f087267a42f8 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Mon, 2 Oct 2023 23:49:40 +0100 Subject: [PATCH 065/116] try free malloc for any field that is a pointer type --- .../SaveScriptableObjectStates.Types.cs | 26 ++++++++++++++++--- .../UnityFix/SaveScriptableObjectStates.cs | 7 +++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs index 0700989aa..ca34b1dd2 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs @@ -3,6 +3,7 @@ using System.Reflection; using HarmonyLib; using UniTAS.Patcher.Models.Utils; +using UniTAS.Patcher.Services; using UniTAS.Patcher.Services.Logging; using UniTAS.Patcher.Utils; using UnityEngine; @@ -20,7 +21,7 @@ private readonly struct StoredState private readonly ILogger _logger; - public StoredState(ScriptableObject scriptableObject, ILogger logger) + public StoredState(ScriptableObject scriptableObject, ILogger logger, ITryFreeMalloc freeMalloc) { ScriptableObject = scriptableObject; _logger = logger; @@ -29,7 +30,7 @@ public StoredState(ScriptableObject scriptableObject, ILogger logger) // TODO limit fields that are serializable to save space var fields = AccessTools.GetDeclaredFields(scriptableObject.GetType()) .Where(x => !x.IsStatic && !x.IsLiteral); - _savedFields = fields.Select(x => new FieldData(x, scriptableObject, logger)).ToArray(); + _savedFields = fields.Select(x => new FieldData(x, scriptableObject, logger, freeMalloc)).ToArray(); } public void Load() @@ -53,12 +54,20 @@ private readonly struct FieldData private readonly Either _value; private readonly FieldInfo _saveField; - public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger) + private readonly ITryFreeMalloc _freeMalloc; + + public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger, ITryFreeMalloc freeMalloc) { _saveField = fieldInfo; _instance = instance; var value = fieldInfo.GetValue(instance); + + if (value.GetType().IsPointer) + { + _freeMalloc = freeMalloc; + } + if (value is Object unityObject) { _value = unityObject; @@ -78,6 +87,17 @@ public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger) public void Load() { + // try to free pointer if it's a pointer + if (_freeMalloc != null) + { + var prevValue = _saveField.GetValue(_instance); + unsafe + { + var prevValueRaw = (IntPtr)Pointer.Unbox(prevValue); + _freeMalloc.TryFree(prevValueRaw); + } + } + _saveField.SetValue(_instance, _value.IsLeft ? _value.Left : _value.Right); } } diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs index 75ebd1bb3..8f82ccba6 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs @@ -2,6 +2,7 @@ using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Interfaces.Events.SoftRestart; using UniTAS.Patcher.Interfaces.Events.UnityEvents.RunEvenPaused; +using UniTAS.Patcher.Services; using UniTAS.Patcher.Services.Logging; using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; using UniTAS.Patcher.Utils; @@ -16,10 +17,12 @@ public partial class SaveScriptableObjectStates : INewScriptableObjectTracker, I private readonly List _destroyObjectsOnRestart = new(); private readonly ILogger _logger; + private readonly ITryFreeMalloc _freeMalloc; - public SaveScriptableObjectStates(ILogger logger) + public SaveScriptableObjectStates(ILogger logger, ITryFreeMalloc freeMalloc) { _logger = logger; + _freeMalloc = freeMalloc; } private bool _initialized; @@ -53,7 +56,7 @@ private void Save(ScriptableObject obj) if (x.ScriptableObject == obj) return; } - _storedStates.Add(new(obj, _logger)); + _storedStates.Add(new(obj, _logger, _freeMalloc)); } public void NewScriptableObject(ScriptableObject scriptableObject) From 50234469823b2e67a292d8223cd45cf2bd026302 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 3 Oct 2023 00:14:23 +0100 Subject: [PATCH 066/116] fixed possible null reference exception on field type get --- .../UnityFix/SaveScriptableObjectStates.Types.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs index ca34b1dd2..fb2a24b79 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs @@ -63,7 +63,7 @@ public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger, var value = fieldInfo.GetValue(instance); - if (value.GetType().IsPointer) + if (fieldInfo.FieldType.IsPointer) { _freeMalloc = freeMalloc; } From 34804bd20e3ccf7e7bbd767c24052da7d3563ce6 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 3 Oct 2023 00:36:55 +0100 Subject: [PATCH 067/116] removed logging --- UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs b/UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs index e4de39bd1..327272cab 100644 --- a/UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs +++ b/UniTAS/Patcher/Patches/Harmony/UnityUnsafeUtilityPatch.cs @@ -32,7 +32,6 @@ private static MethodBase TargetMethod() private static unsafe void Postfix(void* __result, object allocator) { - StaticLogger.Log.LogDebug($"Malloc, with allocator: {allocator}"); var allocatorTranslated = (Allocator)Enum.Parse(typeof(Allocator), allocator.ToString()); UnityMallocTracker.Malloc((IntPtr)__result, allocatorTranslated, false); } @@ -53,7 +52,6 @@ private static MethodBase TargetMethod() private static unsafe void Postfix(void* __result, object allocator) { - StaticLogger.Log.LogDebug($"Malloc tracked, with allocator: {allocator}"); var allocatorTranslated = (Allocator)Enum.Parse(typeof(Allocator), allocator.ToString()); UnityMallocTracker.Malloc((IntPtr)__result, allocatorTranslated, true); } @@ -74,7 +72,6 @@ private static MethodBase TargetMethod() private static unsafe void Prefix(void* memory, object allocator) { - StaticLogger.Log.LogDebug($"Free with allocator: {allocator}"); var allocatorTranslated = (Allocator)Enum.Parse(typeof(Allocator), allocator.ToString()); UnityMallocTracker.Free((IntPtr)memory, allocatorTranslated, false); } @@ -95,7 +92,6 @@ private static MethodBase TargetMethod() private static unsafe void Prefix(void* memory, object allocator) { - StaticLogger.Log.LogDebug($"FreeTracked with allocator: {allocator}"); var allocatorTranslated = (Allocator)Enum.Parse(typeof(Allocator), allocator.ToString()); UnityMallocTracker.Free((IntPtr)memory, allocatorTranslated, true); } From b36fa5e7c21735be3cfcd6353204bbd2416fd1dc Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 5 Oct 2023 00:30:50 +0100 Subject: [PATCH 068/116] extension to get if field is unity serializable --- UniTAS/Patcher/Extensions/FieldInfoExtensions.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 UniTAS/Patcher/Extensions/FieldInfoExtensions.cs diff --git a/UniTAS/Patcher/Extensions/FieldInfoExtensions.cs b/UniTAS/Patcher/Extensions/FieldInfoExtensions.cs new file mode 100644 index 000000000..33dbaa9a3 --- /dev/null +++ b/UniTAS/Patcher/Extensions/FieldInfoExtensions.cs @@ -0,0 +1,15 @@ +using System; +using System.Linq; +using System.Reflection; +using UnityEngine; + +namespace UniTAS.Patcher.Extensions; + +public static class FieldInfoExtensions +{ + public static bool IsFieldUnitySerializable(this FieldInfo field) + { + var attrs = field.GetCustomAttributes(true); + return (field.IsPublic && attrs.Any(x => x is NonSerializedAttribute)) || attrs.Any(x => x is SerializeField); + } +} \ No newline at end of file From d4d0d115d3564be5731c81d4a6e47b82b9f5a52b Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 6 Oct 2023 18:18:16 +0100 Subject: [PATCH 069/116] added unity object referencing --- UniTAS/Patcher/Utils/DeepCopy.cs | 41 +++++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 00487b393..8f7ddd217 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -5,6 +5,8 @@ using System.Reflection; using HarmonyLib; using UniTAS.Patcher.Exceptions; +using UniTAS.Patcher.Extensions; +using Object = UnityEngine.Object; namespace UniTAS.Patcher.Utils; @@ -40,18 +42,37 @@ public static void MakeDeepCopy(object source, out T result, /// The original object /// Optional value transformation function (taking a field name and src/dst instances) /// The optional path root to start with + /// Unity objects in scene to reference to /// The copy of the original object public static object MakeDeepCopy(object source, Func processor = null, - string pathRoot = "") + string pathRoot = "", IEnumerable unityObjects = null) { + var unityObjs = unityObjects?.ToList(); + + // remove itself from the list of unity objects to stop it referencing itself + var selfIndex = unityObjs?.FindIndex(x => ReferenceEquals(x, source)); + Object self = null; + if (selfIndex is >= 0) + { + self = unityObjs[selfIndex.Value]; + unityObjs.RemoveAt(selfIndex.Value); + } + var id = 0ul; - return MakeDeepCopy(source, processor, pathRoot, new(), new(), ref id); + var returnObj = MakeDeepCopy(source, processor, pathRoot, new(), new(), ref id, unityObjs); + + if (self is not null) + { + unityObjs.Add(self); + } + + return returnObj; } // dictionary is used to keep track of instances. int is the ID of the instance, which is used to compare foundReferences and newReferences private static object MakeDeepCopy(object source, Func processor, string pathRoot, Dictionary foundReferences, - Dictionary newReferences, ref ulong id) + Dictionary newReferences, ref ulong id, List unityObjects) { _makeDeepCopyRecursionDepth++; if (_makeDeepCopyRecursionDepth > MAKE_DEEP_COPY_RECURSION_DEPTH_LIMIT) @@ -103,7 +124,7 @@ private static object MakeDeepCopy(object source, Func 0 ? pathRoot + "." + iStr : iStr; var newElement = MakeDeepCopy(array.GetValue(i), processor, path, foundReferences, newReferences, - ref id); + ref id, unityObjects); newArray.SetValue(newElement, i); } @@ -141,7 +162,7 @@ private static object MakeDeepCopy(object source, Func ReferenceEquals(x, value))) + { + // if it is, then just reference it + field.SetValue(result, value); + continue; + } } if (field.FieldType.IsPointer) @@ -197,7 +226,7 @@ private static object MakeDeepCopy(object source, Func Date: Fri, 6 Oct 2023 18:18:29 +0100 Subject: [PATCH 070/116] added ToString to show inner content --- UniTAS/Patcher/Models/Utils/Either.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/UniTAS/Patcher/Models/Utils/Either.cs b/UniTAS/Patcher/Models/Utils/Either.cs index f198fb383..86db5843f 100644 --- a/UniTAS/Patcher/Models/Utils/Either.cs +++ b/UniTAS/Patcher/Models/Utils/Either.cs @@ -29,4 +29,19 @@ public Either(TRight right) public static implicit operator Either(TLeft left) => new(left); public static implicit operator Either(TRight right) => new(right); + + public override string ToString() + { + string value; + try + { + value = (IsLeft ? _left?.ToString() : _right?.ToString()) ?? "null"; + } + catch (Exception) + { + value = "unknown"; + } + + return IsLeft ? $"Left: {value}" : $"Right: {value}"; + } } \ No newline at end of file From 045e12b097faa77b2b0410d5a7c9f9899012fa44 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 6 Oct 2023 18:18:56 +0100 Subject: [PATCH 071/116] fixed logic error --- UniTAS/Patcher/Extensions/FieldInfoExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Extensions/FieldInfoExtensions.cs b/UniTAS/Patcher/Extensions/FieldInfoExtensions.cs index 33dbaa9a3..35bce003e 100644 --- a/UniTAS/Patcher/Extensions/FieldInfoExtensions.cs +++ b/UniTAS/Patcher/Extensions/FieldInfoExtensions.cs @@ -10,6 +10,6 @@ public static class FieldInfoExtensions public static bool IsFieldUnitySerializable(this FieldInfo field) { var attrs = field.GetCustomAttributes(true); - return (field.IsPublic && attrs.Any(x => x is NonSerializedAttribute)) || attrs.Any(x => x is SerializeField); + return (field.IsPublic && !attrs.Any(x => x is NonSerializedAttribute)) || attrs.Any(x => x is SerializeField); } } \ No newline at end of file From f440617b39ea3e3865b58272bce79b6f42a9d26d Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 6 Oct 2023 18:23:30 +0100 Subject: [PATCH 072/116] added try free for field instead --- UniTAS/Patcher/Implementations/MallocTracker.cs | 14 +++++++++++++- UniTAS/Patcher/Services/ITryFreeMalloc.cs | 6 +++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/UniTAS/Patcher/Implementations/MallocTracker.cs b/UniTAS/Patcher/Implementations/MallocTracker.cs index 3bdc920dd..76dec2a48 100644 --- a/UniTAS/Patcher/Implementations/MallocTracker.cs +++ b/UniTAS/Patcher/Implementations/MallocTracker.cs @@ -33,13 +33,25 @@ public MallocTracker(ILogger logger) _hasUnityMalloc = _unityFree != null && _unityFreeTracked != null && _allocator != null; } - public void TryFree(IntPtr ptr) + private void TryFree(IntPtr ptr) { if (_hasUnityMalloc && TryFreeUnityMalloc(ptr)) return; _logger.LogWarning($"Could not find malloc at {ptr} to free"); } + public void TryFree(object instance, FieldInfo fieldInfo) + { + if (!fieldInfo.FieldType.IsPointer) return; + + var prevValue = fieldInfo.GetValue(instance); + unsafe + { + var prevValueRaw = (IntPtr)Pointer.Unbox(prevValue); + TryFree(prevValueRaw); + } + } + private bool TryFreeUnityMalloc(IntPtr ptr) { var unityMallocIndex = _unityMallocs.FindIndex(x => x.Ptr == ptr); diff --git a/UniTAS/Patcher/Services/ITryFreeMalloc.cs b/UniTAS/Patcher/Services/ITryFreeMalloc.cs index 81c978936..f16cc3ced 100644 --- a/UniTAS/Patcher/Services/ITryFreeMalloc.cs +++ b/UniTAS/Patcher/Services/ITryFreeMalloc.cs @@ -1,11 +1,11 @@ -using System; +using System.Reflection; namespace UniTAS.Patcher.Services; public interface ITryFreeMalloc { /// - /// Tries to free the memory at the given pointer + /// Tries to free the memory of the given field /// - void TryFree(IntPtr ptr); + void TryFree(object instance, FieldInfo fieldInfo); } \ No newline at end of file From ec2c990624af0bd028e4730872d8f8b1de9cfc51 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 6 Oct 2023 21:20:49 +0100 Subject: [PATCH 073/116] added getting fields recursively --- UniTAS/Patcher/Extensions/TypeExtensions.cs | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 UniTAS/Patcher/Extensions/TypeExtensions.cs diff --git a/UniTAS/Patcher/Extensions/TypeExtensions.cs b/UniTAS/Patcher/Extensions/TypeExtensions.cs new file mode 100644 index 000000000..cb61e1a04 --- /dev/null +++ b/UniTAS/Patcher/Extensions/TypeExtensions.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Reflection; + +namespace UniTAS.Patcher.Extensions; + +public static class TypeExtensions +{ + public static FieldInfo[] GetFieldsRecursive(this Type type, BindingFlags bindingFlags) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + + var fields = new List(); + var currentType = type; + + while (currentType != null) + { + fields.AddRange(currentType.GetFields(bindingFlags)); + currentType = currentType.BaseType; + } + + return fields.ToArray(); + } +} \ No newline at end of file From aae193fce80ceabc4030657fc34eea6fe4f94c55 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 6 Oct 2023 21:22:39 +0100 Subject: [PATCH 074/116] addition of storing which fields to set default to --- .../SaveScriptableObjectStates.Types.cs | 71 ++++++++++++------- .../UnityFix/SaveScriptableObjectStates.cs | 21 ++++-- 2 files changed, 62 insertions(+), 30 deletions(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs index fb2a24b79..d4c1481e7 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs @@ -1,7 +1,9 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Reflection; using HarmonyLib; +using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Models.Utils; using UniTAS.Patcher.Services; using UniTAS.Patcher.Services.Logging; @@ -18,23 +20,45 @@ private readonly struct StoredState public readonly ScriptableObject ScriptableObject; private readonly FieldData[] _savedFields; + private readonly FieldInfo[] _resetFields; private readonly ILogger _logger; + private readonly ITryFreeMalloc _freeMalloc; - public StoredState(ScriptableObject scriptableObject, ILogger logger, ITryFreeMalloc freeMalloc) + public StoredState(ScriptableObject scriptableObject, Object[] allObjs, ILogger logger, + ITryFreeMalloc freeMalloc) { ScriptableObject = scriptableObject; _logger = logger; + _freeMalloc = freeMalloc; // save - // TODO limit fields that are serializable to save space - var fields = AccessTools.GetDeclaredFields(scriptableObject.GetType()) - .Where(x => !x.IsStatic && !x.IsLiteral); - _savedFields = fields.Select(x => new FieldData(x, scriptableObject, logger, freeMalloc)).ToArray(); + var fields = scriptableObject.GetType().GetFieldsRecursive(AccessTools.all) + .Where(x => !x.IsStatic && !x.IsLiteral && x.DeclaringType != typeof(Object)); + + var resetFields = new List(); + var savedFields = new List(); + + foreach (var field in fields) + { + if (field.IsFieldUnitySerializable()) + { + savedFields.Add(new(field, scriptableObject, allObjs, logger, freeMalloc)); + continue; + } + + resetFields.Add(field); + } + + _savedFields = savedFields.ToArray(); + _resetFields = resetFields.ToArray(); } public void Load() { + StaticLogger.Log.LogDebug( + $"loading state for type {ScriptableObject.GetType().FullName}, ({ScriptableObject.name}), field count: {_savedFields.Length}, reset field count: {_resetFields.Length}"); + if (ScriptableObject == null) { _logger.LogError("ScriptableObject is null, this should not happen"); @@ -45,6 +69,13 @@ public void Load() { savedField.Load(); } + + foreach (var resetField in _resetFields) + { + StaticLogger.Log.LogDebug($"Resetting field {resetField.Name}"); + _freeMalloc?.TryFree(ScriptableObject, resetField); + resetField.SetValue(ScriptableObject, null); + } } } @@ -56,7 +87,8 @@ private readonly struct FieldData private readonly ITryFreeMalloc _freeMalloc; - public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger, ITryFreeMalloc freeMalloc) + public FieldData(FieldInfo fieldInfo, ScriptableObject instance, Object[] allObjs, ILogger logger, + ITryFreeMalloc freeMalloc) { _saveField = fieldInfo; _instance = instance; @@ -68,35 +100,22 @@ public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger, _freeMalloc = freeMalloc; } - if (value is Object unityObject) + try { - _value = unityObject; + _value = DeepCopy.MakeDeepCopy(value, unityObjects: allObjs); } - else + catch (Exception e) { - try - { - _value = DeepCopy.MakeDeepCopy(value); - } - catch (Exception e) - { - logger.LogError($"Failed to deep copy field value, type is {fieldInfo.FieldType.FullName}, {e}"); - } + logger.LogError($"Failed to deep copy field value, type is {fieldInfo.FieldType.FullName}, {e}"); } } public void Load() { + StaticLogger.Log.LogDebug($"Loading field {_saveField.Name}, value is {_value}"); + // try to free pointer if it's a pointer - if (_freeMalloc != null) - { - var prevValue = _saveField.GetValue(_instance); - unsafe - { - var prevValueRaw = (IntPtr)Pointer.Unbox(prevValue); - _freeMalloc.TryFree(prevValueRaw); - } - } + _freeMalloc?.TryFree(_instance, _saveField); _saveField.SetValue(_instance, _value.IsLeft ? _value.Left : _value.Right); } diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs index 8f82ccba6..b385ebf44 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs @@ -1,4 +1,7 @@ using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using HarmonyLib; using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Interfaces.Events.SoftRestart; using UniTAS.Patcher.Interfaces.Events.UnityEvents.RunEvenPaused; @@ -19,10 +22,17 @@ public partial class SaveScriptableObjectStates : INewScriptableObjectTracker, I private readonly ILogger _logger; private readonly ITryFreeMalloc _freeMalloc; + private readonly Assembly[] _ignoreAssemblies; + public SaveScriptableObjectStates(ILogger logger, ITryFreeMalloc freeMalloc) { _logger = logger; _freeMalloc = freeMalloc; + + _ignoreAssemblies = new[] + { + AccessTools.TypeByName("TMPro.TMP_FontAsset") + }.Where(x => x != null).Select(x => x.Assembly).ToArray(); } private bool _initialized; @@ -40,23 +50,26 @@ private void SaveAll() { _logger.LogDebug("Saving all ScriptableObject states"); - var allScriptableObjects = ResourcesUtils.FindObjectsOfTypeAll(); + var allObjs = ResourcesUtils.FindObjectsOfTypeAll(); + var allScriptableObjects = allObjs.Where(x => + x is ScriptableObject && _ignoreAssemblies.All(a => !Equals(a, x.GetType().Assembly))) + .Cast().ToArray(); _logger.LogDebug($"Found {allScriptableObjects.Length} ScriptableObjects"); foreach (var obj in allScriptableObjects) { - Save(obj); + Save(obj, allObjs); } } - private void Save(ScriptableObject obj) + private void Save(ScriptableObject obj, Object[] allObjs) { foreach (var x in _storedStates) { if (x.ScriptableObject == obj) return; } - _storedStates.Add(new(obj, _logger, _freeMalloc)); + _storedStates.Add(new(obj, allObjs, _logger, _freeMalloc)); } public void NewScriptableObject(ScriptableObject scriptableObject) From 8d5585343804a891a78aa56119249a40be85ef3e Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 6 Oct 2023 21:22:53 +0100 Subject: [PATCH 075/116] removed file --- .../NewInputSystem/OverrideInputOnMovie.cs | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 UniTAS/Patcher/Implementations/NewInputSystem/OverrideInputOnMovie.cs diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/OverrideInputOnMovie.cs b/UniTAS/Patcher/Implementations/NewInputSystem/OverrideInputOnMovie.cs deleted file mode 100644 index cf99ff73a..000000000 --- a/UniTAS/Patcher/Implementations/NewInputSystem/OverrideInputOnMovie.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using UniTAS.Patcher.Interfaces.DependencyInjection; -using UniTAS.Patcher.Services; -using UniTAS.Patcher.Services.Movie; -using UniTAS.Patcher.Services.NewInputSystem; - -namespace UniTAS.Patcher.Implementations.NewInputSystem; - -[Singleton] -[ForceInstantiate] -public class OverrideInputOnMovie -{ - private readonly IInputSystemOverride _inputSystemOverride; - - public OverrideInputOnMovie(IGameRestart gameRestart, IInputSystemOverride inputSystemOverride, - IMovieRunnerEvents movieEvents) - { - _inputSystemOverride = inputSystemOverride; - gameRestart.OnGameRestartResume += OnGameRestartResume; - movieEvents.OnMovieEnd += OnMovieEnd; - } - - private void OnGameRestartResume(DateTime startupTime, bool preMonoBehaviourResume) - { - if (!preMonoBehaviourResume) return; - _inputSystemOverride.Override = true; - } - - private void OnMovieEnd() - { - _inputSystemOverride.Override = false; - } -} \ No newline at end of file From 5eb2bde5394411ebecef1d182c5fb88088903dab Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 7 Oct 2023 02:16:15 +0100 Subject: [PATCH 076/116] reverted referencing scene objects --- .../SaveScriptableObjectStates.Types.cs | 10 ++--- .../UnityFix/SaveScriptableObjectStates.cs | 12 +++--- UniTAS/Patcher/Utils/DeepCopy.cs | 41 +++---------------- 3 files changed, 15 insertions(+), 48 deletions(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs index d4c1481e7..b6feb105f 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs @@ -25,8 +25,7 @@ private readonly struct StoredState private readonly ILogger _logger; private readonly ITryFreeMalloc _freeMalloc; - public StoredState(ScriptableObject scriptableObject, Object[] allObjs, ILogger logger, - ITryFreeMalloc freeMalloc) + public StoredState(ScriptableObject scriptableObject, ILogger logger, ITryFreeMalloc freeMalloc) { ScriptableObject = scriptableObject; _logger = logger; @@ -43,7 +42,7 @@ public StoredState(ScriptableObject scriptableObject, Object[] allObjs, ILogger { if (field.IsFieldUnitySerializable()) { - savedFields.Add(new(field, scriptableObject, allObjs, logger, freeMalloc)); + savedFields.Add(new(field, scriptableObject, logger, freeMalloc)); continue; } @@ -87,8 +86,7 @@ private readonly struct FieldData private readonly ITryFreeMalloc _freeMalloc; - public FieldData(FieldInfo fieldInfo, ScriptableObject instance, Object[] allObjs, ILogger logger, - ITryFreeMalloc freeMalloc) + public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger, ITryFreeMalloc freeMalloc) { _saveField = fieldInfo; _instance = instance; @@ -102,7 +100,7 @@ public FieldData(FieldInfo fieldInfo, ScriptableObject instance, Object[] allObj try { - _value = DeepCopy.MakeDeepCopy(value, unityObjects: allObjs); + _value = DeepCopy.MakeDeepCopy(value); } catch (Exception e) { diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs index b385ebf44..ae895c1da 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.cs @@ -50,26 +50,24 @@ private void SaveAll() { _logger.LogDebug("Saving all ScriptableObject states"); - var allObjs = ResourcesUtils.FindObjectsOfTypeAll(); - var allScriptableObjects = allObjs.Where(x => - x is ScriptableObject && _ignoreAssemblies.All(a => !Equals(a, x.GetType().Assembly))) - .Cast().ToArray(); + var allScriptableObjects = ResourcesUtils.FindObjectsOfTypeAll() + .Where(x => _ignoreAssemblies.All(a => !Equals(a, x.GetType().Assembly))).ToArray(); _logger.LogDebug($"Found {allScriptableObjects.Length} ScriptableObjects"); foreach (var obj in allScriptableObjects) { - Save(obj, allObjs); + Save(obj); } } - private void Save(ScriptableObject obj, Object[] allObjs) + private void Save(ScriptableObject obj) { foreach (var x in _storedStates) { if (x.ScriptableObject == obj) return; } - _storedStates.Add(new(obj, allObjs, _logger, _freeMalloc)); + _storedStates.Add(new(obj, _logger, _freeMalloc)); } public void NewScriptableObject(ScriptableObject scriptableObject) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 8f7ddd217..00487b393 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -5,8 +5,6 @@ using System.Reflection; using HarmonyLib; using UniTAS.Patcher.Exceptions; -using UniTAS.Patcher.Extensions; -using Object = UnityEngine.Object; namespace UniTAS.Patcher.Utils; @@ -42,37 +40,18 @@ public static void MakeDeepCopy(object source, out T result, /// The original object /// Optional value transformation function (taking a field name and src/dst instances) /// The optional path root to start with - /// Unity objects in scene to reference to /// The copy of the original object public static object MakeDeepCopy(object source, Func processor = null, - string pathRoot = "", IEnumerable unityObjects = null) + string pathRoot = "") { - var unityObjs = unityObjects?.ToList(); - - // remove itself from the list of unity objects to stop it referencing itself - var selfIndex = unityObjs?.FindIndex(x => ReferenceEquals(x, source)); - Object self = null; - if (selfIndex is >= 0) - { - self = unityObjs[selfIndex.Value]; - unityObjs.RemoveAt(selfIndex.Value); - } - var id = 0ul; - var returnObj = MakeDeepCopy(source, processor, pathRoot, new(), new(), ref id, unityObjs); - - if (self is not null) - { - unityObjs.Add(self); - } - - return returnObj; + return MakeDeepCopy(source, processor, pathRoot, new(), new(), ref id); } // dictionary is used to keep track of instances. int is the ID of the instance, which is used to compare foundReferences and newReferences private static object MakeDeepCopy(object source, Func processor, string pathRoot, Dictionary foundReferences, - Dictionary newReferences, ref ulong id, List unityObjects) + Dictionary newReferences, ref ulong id) { _makeDeepCopyRecursionDepth++; if (_makeDeepCopyRecursionDepth > MAKE_DEEP_COPY_RECURSION_DEPTH_LIMIT) @@ -124,7 +103,7 @@ private static object MakeDeepCopy(object source, Func 0 ? pathRoot + "." + iStr : iStr; var newElement = MakeDeepCopy(array.GetValue(i), processor, path, foundReferences, newReferences, - ref id, unityObjects); + ref id); newArray.SetValue(newElement, i); } @@ -162,7 +141,7 @@ private static object MakeDeepCopy(object source, Func ReferenceEquals(x, value))) - { - // if it is, then just reference it - field.SetValue(result, value); - continue; - } } if (field.FieldType.IsPointer) @@ -226,7 +197,7 @@ private static object MakeDeepCopy(object source, Func Date: Sat, 7 Oct 2023 02:29:24 +0100 Subject: [PATCH 077/116] don't clone if derived from UnityEngine.Object --- UniTAS/Patcher/Utils/DeepCopy.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 00487b393..ebe540f19 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -5,6 +5,8 @@ using System.Reflection; using HarmonyLib; using UniTAS.Patcher.Exceptions; +using UnityEngine; +using Object = UnityEngine.Object; namespace UniTAS.Patcher.Utils; @@ -53,6 +55,12 @@ private static object MakeDeepCopy(object source, Func foundReferences, Dictionary newReferences, ref ulong id) { + if (source is Object and not MonoBehaviour and not ScriptableObject) + { + // this is a native unity object, so we can't make a deep copy of it + return source; + } + _makeDeepCopyRecursionDepth++; if (_makeDeepCopyRecursionDepth > MAKE_DEEP_COPY_RECURSION_DEPTH_LIMIT) { From 351d3c4ac59e657ace8222512f72e332714bf76f Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 8 Oct 2023 05:29:28 +0100 Subject: [PATCH 078/116] addition of a cool line call trace!!! --- UniTAS/Patcher/Utils/StaticLogger.cs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/UniTAS/Patcher/Utils/StaticLogger.cs b/UniTAS/Patcher/Utils/StaticLogger.cs index e24096630..2cf923d4b 100644 --- a/UniTAS/Patcher/Utils/StaticLogger.cs +++ b/UniTAS/Patcher/Utils/StaticLogger.cs @@ -1,4 +1,6 @@ +using System; using System.Diagnostics; +using System.Runtime.CompilerServices; using BepInEx.Logging; namespace UniTAS.Patcher.Utils; @@ -12,10 +14,21 @@ public static class StaticLogger #endif [Conditional("TRACE")] - public static void Trace(object data) + public static void Trace(object data, [CallerLineNumber] int lineNumber = 0, [CallerFilePath] string path = null) { #if TRACE - TraceLog.LogDebug(data); + // path would be like "some_path/UniTAS/UniTAS/Patcher/Utils/StaticLogger.cs" + // removal from "some_path/UniTAS/UniTAS" to "Patcher/Utils/StaticLogger.cs" + if (path != null) + { + var patcherIndex = path.IndexOf("Patcher", StringComparison.InvariantCulture); + if (patcherIndex >= 0) + { + path = path.Substring(patcherIndex + "Patcher".Length + 1); + } + } + + TraceLog.LogDebug($"[{path}:{lineNumber}] {data}"); #endif } } \ No newline at end of file From 8680217196143c221bf4c96fcc7c42f45318b6d8 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 8 Oct 2023 05:30:00 +0100 Subject: [PATCH 079/116] addition of trace logging --- UniTAS/Patcher/Utils/DeepCopy.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index ebe540f19..3dbfb4270 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -55,9 +55,12 @@ private static object MakeDeepCopy(object source, Func foundReferences, Dictionary newReferences, ref ulong id) { + StaticLogger.Trace($"MakeDeepCopy, type: {source?.GetType().FullName}, pathRoot: {pathRoot}"); + if (source is Object and not MonoBehaviour and not ScriptableObject) { // this is a native unity object, so we can't make a deep copy of it + StaticLogger.Trace("MakeDeepCopy, skipping native unity object"); return source; } @@ -71,6 +74,7 @@ private static object MakeDeepCopy(object source, Func Date: Sun, 8 Oct 2023 05:39:20 +0100 Subject: [PATCH 080/116] addition of depth log --- UniTAS/Patcher/Utils/DeepCopy.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 3dbfb4270..e39f0769c 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -55,7 +55,8 @@ private static object MakeDeepCopy(object source, Func foundReferences, Dictionary newReferences, ref ulong id) { - StaticLogger.Trace($"MakeDeepCopy, type: {source?.GetType().FullName}, pathRoot: {pathRoot}"); + StaticLogger.Trace( + $"MakeDeepCopy, depth: {_makeDeepCopyRecursionDepth}, type: {source?.GetType().FullName}, pathRoot: {pathRoot}"); if (source is Object and not MonoBehaviour and not ScriptableObject) { From 8ef6d740a93cda579c62e411deb7249025219ba5 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sun, 8 Oct 2023 05:54:07 +0100 Subject: [PATCH 081/116] more logs --- UniTAS/Patcher/Utils/DeepCopy.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index e39f0769c..6374d5a77 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -108,6 +108,7 @@ private static object MakeDeepCopy(object source, Func 0 ? pathRoot + "." + name : name; object value; if (processor is not null) { + StaticLogger.Trace("MakeDeepCopy, using processor to get value for copying"); var srcTraverse = Traverse.Create(source).Field(name); var dstTraverse = Traverse.Create(result).Field(name); @@ -209,6 +215,7 @@ private static object MakeDeepCopy(object source, Func Date: Thu, 12 Oct 2023 13:37:39 +0100 Subject: [PATCH 082/116] cleaned up target dll info to be merged with TargetPatcherDlls --- .../Preloader/FinalizeSuppressionPatch.cs | 10 +---- .../Patches/Preloader/StaticCtorHeaders.cs | 10 +---- .../Utils/StaticCtorPatchTargetInfo.cs | 15 -------- UniTAS/Patcher/Utils/TargetPatcherDlls.cs | 37 +++++++++++++++++++ 4 files changed, 39 insertions(+), 33 deletions(-) delete mode 100644 UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs diff --git a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs index d24acae27..218528b1d 100644 --- a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs @@ -1,12 +1,10 @@ using System.Collections.Generic; -using System.IO; using System.Linq; using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; using UniTAS.Patcher.ContainerBindings.GameExecutionControllers; -using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Interfaces; using UniTAS.Patcher.Utils; @@ -14,13 +12,7 @@ namespace UniTAS.Patcher.Patches.Preloader; public class FinalizeSuppressionPatch : PreloadPatcher { - public override IEnumerable TargetDLLs => TargetPatcherDlls.AllDLLs.Where(x => - { - var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); - return fileWithoutExtension == null || - // StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || - !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); - }); + public override IEnumerable TargetDLLs => TargetPatcherDlls.TargetDllsWithExclusions; public override void Patch(ref AssemblyDefinition assembly) { diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index 36c0e4131..2a9237f35 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -2,14 +2,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Linq; using System.Reflection; using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; -using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Interfaces; using UniTAS.Patcher.ManualServices.Trackers; using UniTAS.Patcher.Utils; @@ -20,13 +18,7 @@ namespace UniTAS.Patcher.Patches.Preloader; [SuppressMessage("ReSharper", "UnusedType.Global")] public class StaticCtorHeaders : PreloadPatcher { - public override IEnumerable TargetDLLs => TargetPatcherDlls.AllDLLs.Where(x => - { - var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); - return fileWithoutExtension == null || - // StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || - !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); - }); + public override IEnumerable TargetDLLs => TargetPatcherDlls.TargetDllsWithExclusions; public override void Patch(ref AssemblyDefinition assembly) { diff --git a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs deleted file mode 100644 index 03de19e9b..000000000 --- a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniTAS.Patcher.Utils; - -public static class StaticCtorPatchTargetInfo -{ - public static string[] AssemblyExclusionsRaw { get; } = - { - // c# related - "System.*", - "System", - "netstandard", - "mscorlib", - "Mono.*", - "Mono" - }; -} \ No newline at end of file diff --git a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs index b1b050ec3..7f5c8a4f7 100644 --- a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs +++ b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using BepInEx; +using UniTAS.Patcher.Extensions; namespace UniTAS.Patcher.Utils; @@ -10,4 +11,40 @@ public static class TargetPatcherDlls public static IEnumerable AllDLLs => Directory.GetFiles(Paths.ManagedPath, "*.dll", SearchOption.TopDirectoryOnly).Select(Path.GetFileName) .ToArray(); + + + public static string[] TargetDllsWithExclusions { get; } = AllDLLs.Where(x => + { + var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); + return fileWithoutExtension == null || + AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + !AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); + }).ToArray(); + + private static string[] AssemblyExclusionsRaw { get; } = + { + "UnityEngine.*", + "UnityEngine", + "Unity.*", + "System.*", + "System", + "netstandard", + "mscorlib", + "Mono.*", + "Mono", + "MonoMod.*", + "BepInEx.*", + "BepInEx", + "MonoMod.*", + "0Harmony", + "HarmonyXInterop", + "StructureMap", + "Newtonsoft.Json" + }; + + private static string[] AssemblyIncludeRaw { get; } = + { + "Unity.InputSystem", + "UnityEngine.InputModule" + }; } \ No newline at end of file From b54cd44aa4490ffd714474f20a503b5fbd85f959 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 12 Oct 2023 23:52:42 +0100 Subject: [PATCH 083/116] extension to check if TypeDefinition is derived from MonoBehaviour --- .../Extensions/TypeDefinitionExtensions.cs | 27 +++++++++++++++++++ .../Patches/Preloader/MonoBehaviourPatch.cs | 17 +----------- 2 files changed, 28 insertions(+), 16 deletions(-) create mode 100644 UniTAS/Patcher/Extensions/TypeDefinitionExtensions.cs diff --git a/UniTAS/Patcher/Extensions/TypeDefinitionExtensions.cs b/UniTAS/Patcher/Extensions/TypeDefinitionExtensions.cs new file mode 100644 index 000000000..5c2f11943 --- /dev/null +++ b/UniTAS/Patcher/Extensions/TypeDefinitionExtensions.cs @@ -0,0 +1,27 @@ +using Mono.Cecil; +using MonoMod.Utils; +using UnityEngine; + +namespace UniTAS.Patcher.Extensions; + +public static class TypeDefinitionExtensions +{ + /// + /// Checks if the type is a subclass of + /// + public static bool IsMonoBehaviour(this TypeDefinition type) + { + var baseType = type.BaseType?.SafeResolve(); + while (baseType != null) + { + if (baseType.FullName == "UnityEngine.MonoBehaviour") + { + return true; + } + + baseType = baseType.BaseType?.SafeResolve(); + } + + return false; + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs index 24f68dd6a..b07edb662 100644 --- a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs @@ -6,7 +6,6 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; -using MonoMod.Utils; using UniTAS.Patcher.ContainerBindings.GameExecutionControllers; using UniTAS.Patcher.ContainerBindings.UnityEvents; using UniTAS.Patcher.Extensions; @@ -140,21 +139,7 @@ public override void Patch(ref AssemblyDefinition assembly) foreach (var type in types) { - // check if type base is MonoBehaviour - var isMonoBehaviour = false; - var baseType = type.BaseType?.SafeResolve(); - while (baseType != null) - { - if (baseType.FullName == "UnityEngine.MonoBehaviour") - { - isMonoBehaviour = true; - break; - } - - baseType = baseType.BaseType?.SafeResolve(); - } - - if (!isMonoBehaviour) continue; + if (!type.IsMonoBehaviour()) continue; StaticLogger.Log.LogDebug($"Patching MonoBehaviour type: {type.FullName}"); From f09ac4d75698b4e2f22f5163bbdeb9cc97ca21e7 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 12 Oct 2023 23:52:50 +0100 Subject: [PATCH 084/116] added 3 extra ways to init unitas --- .../Patches/Preloader/UnityInitInvoke.cs | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs b/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs index b58bc4231..cedf20b2d 100644 --- a/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs +++ b/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs @@ -7,6 +7,7 @@ using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Rocks; +using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Interfaces; using UniTAS.Patcher.Utils; @@ -26,13 +27,17 @@ public UnityInitInvoke() var targetDLLs = foundEntryAssembly != null ? new[] { foundEntryAssembly } : new[] { "UnityEngine.CoreModule.dll", "UnityEngine.dll" }; + + // add patch target dlls too + targetDLLs = targetDLLs.Concat(TargetPatcherDlls.TargetDllsWithExclusions).Distinct().ToArray(); TargetDLLs = targetDLLs; + _targetClass = GetEntryKey(bepInExConfig, entryPoint, "Type") ?? // better late than never "UnityEngine.Camera"; _targetMethod = GetEntryKey(bepInExConfig, entryPoint, "Method") ?? ".cctor"; StaticLogger.Log.LogInfo( - $"UniTAS will be hooked on {_targetClass}.{_targetMethod} in {string.Join(", ", targetDLLs)}"); + $"UniTAS will be hooked on {_targetClass}.{_targetMethod} in {string.Join(", ", targetDLLs)} as a last resort init hook"); } // why can't I even load config with BepInEx's own ConfigFile???? @@ -57,6 +62,54 @@ private static string GetEntryKey(string configRaw, string entry, string key) public override void Patch(ref AssemblyDefinition assembly) { + TryHookAwakes(assembly); + TryHookRuntimeInits(assembly); + TryHookLastResort(assembly); + } + + private static void TryHookRuntimeInits(AssemblyDefinition assembly) + { + StaticLogger.Log.LogDebug("Trying to hook all RuntimeInitializeOnLoadMethodAttribute methods"); + + var types = assembly.MainModule.GetAllTypes(); + + foreach (var type in types) + { + // find all methods with RuntimeInitializeOnLoadMethodAttribute + var initMethods = type.GetMethods().Where(x => + x.CustomAttributes.Any(a => + a.AttributeType.FullName == "UnityEngine.RuntimeInitializeOnLoadMethodAttribute")); + + foreach (var initMethod in initMethods) + { + ILCodeUtils.MethodInvokeHook(assembly, initMethod, + AccessTools.Method(typeof(InvokeTracker), nameof(InvokeTracker.OnUnityInit))); + LogHook(assembly, type.Name, initMethod.Name); + } + } + } + + private static void TryHookAwakes(AssemblyDefinition assembly) + { + StaticLogger.Log.LogDebug("Trying to hook all MonoBehaviours Awake methods"); + + var types = assembly.MainModule.GetAllTypes().Where(x => x.IsMonoBehaviour()); + + foreach (var type in types) + { + var awake = type.GetMethods().FirstOrDefault(x => x.Name == "Awake" && !x.IsStatic && !x.HasParameters); + if (awake == null) continue; + + ILCodeUtils.MethodInvokeHook(assembly, awake, + AccessTools.Method(typeof(InvokeTracker), nameof(InvokeTracker.OnUnityInit))); + LogHook(assembly, type.Name, "Awake"); + } + } + + private void TryHookLastResort(AssemblyDefinition assembly) + { + StaticLogger.Log.LogDebug("Trying to hook last resort init method defined in BepInEx config"); + // make sure it exists var targetType = assembly.MainModule.GetAllTypes().FirstOrDefault(t => t.Name == _targetClass); if (targetType == null) return; From 4277ce2169890977f31bf4654578ebf5ee475a1a Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 14 Oct 2023 08:05:58 +0100 Subject: [PATCH 085/116] Revert "cleaned up target dll info to be merged with TargetPatcherDlls" This reverts commit 2b89617c208a83cadda3052da27f9e22006297f8. --- .../Preloader/FinalizeSuppressionPatch.cs | 10 ++++- .../Patches/Preloader/StaticCtorHeaders.cs | 10 ++++- .../Utils/StaticCtorPatchTargetInfo.cs | 31 ++++++++++++++++ UniTAS/Patcher/Utils/TargetPatcherDlls.cs | 37 ------------------- 4 files changed, 49 insertions(+), 39 deletions(-) create mode 100644 UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs diff --git a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs index 218528b1d..35f70dbdd 100644 --- a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs @@ -1,10 +1,12 @@ using System.Collections.Generic; +using System.IO; using System.Linq; using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; using UniTAS.Patcher.ContainerBindings.GameExecutionControllers; +using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Interfaces; using UniTAS.Patcher.Utils; @@ -12,7 +14,13 @@ namespace UniTAS.Patcher.Patches.Preloader; public class FinalizeSuppressionPatch : PreloadPatcher { - public override IEnumerable TargetDLLs => TargetPatcherDlls.TargetDllsWithExclusions; + public override IEnumerable TargetDLLs => TargetPatcherDlls.AllDLLs.Where(x => + { + var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); + return fileWithoutExtension == null || + StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); + }); public override void Patch(ref AssemblyDefinition assembly) { diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index 2a9237f35..c093af09f 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -2,12 +2,14 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Linq; using System.Reflection; using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; +using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Interfaces; using UniTAS.Patcher.ManualServices.Trackers; using UniTAS.Patcher.Utils; @@ -18,7 +20,13 @@ namespace UniTAS.Patcher.Patches.Preloader; [SuppressMessage("ReSharper", "UnusedType.Global")] public class StaticCtorHeaders : PreloadPatcher { - public override IEnumerable TargetDLLs => TargetPatcherDlls.TargetDllsWithExclusions; + public override IEnumerable TargetDLLs => TargetPatcherDlls.AllDLLs.Where(x => + { + var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); + return fileWithoutExtension == null || + StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); + }); public override void Patch(ref AssemblyDefinition assembly) { diff --git a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs new file mode 100644 index 000000000..a629fdf4b --- /dev/null +++ b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs @@ -0,0 +1,31 @@ +namespace UniTAS.Patcher.Utils; + +public static class StaticCtorPatchTargetInfo +{ + public static string[] AssemblyExclusionsRaw { get; } = + { + "UnityEngine.*", + "UnityEngine", + "Unity.*", + "System.*", + "System", + "netstandard", + "mscorlib", + "Mono.*", + "Mono", + "MonoMod.*", + "BepInEx.*", + "BepInEx", + "MonoMod.*", + "0Harmony", + "HarmonyXInterop", + "StructureMap", + "Newtonsoft.Json" + }; + + public static string[] AssemblyIncludeRaw { get; } = + { + "Unity.InputSystem", + "UnityEngine.InputModule" + }; +} \ No newline at end of file diff --git a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs index 7f5c8a4f7..b1b050ec3 100644 --- a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs +++ b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs @@ -2,7 +2,6 @@ using System.IO; using System.Linq; using BepInEx; -using UniTAS.Patcher.Extensions; namespace UniTAS.Patcher.Utils; @@ -11,40 +10,4 @@ public static class TargetPatcherDlls public static IEnumerable AllDLLs => Directory.GetFiles(Paths.ManagedPath, "*.dll", SearchOption.TopDirectoryOnly).Select(Path.GetFileName) .ToArray(); - - - public static string[] TargetDllsWithExclusions { get; } = AllDLLs.Where(x => - { - var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); - return fileWithoutExtension == null || - AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || - !AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); - }).ToArray(); - - private static string[] AssemblyExclusionsRaw { get; } = - { - "UnityEngine.*", - "UnityEngine", - "Unity.*", - "System.*", - "System", - "netstandard", - "mscorlib", - "Mono.*", - "Mono", - "MonoMod.*", - "BepInEx.*", - "BepInEx", - "MonoMod.*", - "0Harmony", - "HarmonyXInterop", - "StructureMap", - "Newtonsoft.Json" - }; - - private static string[] AssemblyIncludeRaw { get; } = - { - "Unity.InputSystem", - "UnityEngine.InputModule" - }; } \ No newline at end of file From 5173a802ed83f3ee345e68a4ed9f559d7e4640be Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 17 Oct 2023 06:28:23 +0100 Subject: [PATCH 086/116] changed unity init hook point --- .../Patches/Preloader/UnityInitInvoke.cs | 53 +++++++++++-------- UniTAS/Patcher/Utils/InvokeTracker.cs | 3 +- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs b/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs index cedf20b2d..305cc6277 100644 --- a/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs +++ b/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs @@ -29,7 +29,13 @@ public UnityInitInvoke() : new[] { "UnityEngine.CoreModule.dll", "UnityEngine.dll" }; // add patch target dlls too - targetDLLs = targetDLLs.Concat(TargetPatcherDlls.TargetDllsWithExclusions).Distinct().ToArray(); + targetDLLs = targetDLLs.Concat(TargetPatcherDlls.AllDLLs.Where(x => + { + var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); + return fileWithoutExtension == null || + StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); + })).Distinct().ToArray(); TargetDLLs = targetDLLs; _targetClass = GetEntryKey(bepInExConfig, entryPoint, "Type") ?? @@ -63,31 +69,32 @@ private static string GetEntryKey(string configRaw, string entry, string key) public override void Patch(ref AssemblyDefinition assembly) { TryHookAwakes(assembly); - TryHookRuntimeInits(assembly); + // TryHookRuntimeInits(assembly); TryHookLastResort(assembly); } - private static void TryHookRuntimeInits(AssemblyDefinition assembly) - { - StaticLogger.Log.LogDebug("Trying to hook all RuntimeInitializeOnLoadMethodAttribute methods"); - - var types = assembly.MainModule.GetAllTypes(); - - foreach (var type in types) - { - // find all methods with RuntimeInitializeOnLoadMethodAttribute - var initMethods = type.GetMethods().Where(x => - x.CustomAttributes.Any(a => - a.AttributeType.FullName == "UnityEngine.RuntimeInitializeOnLoadMethodAttribute")); - - foreach (var initMethod in initMethods) - { - ILCodeUtils.MethodInvokeHook(assembly, initMethod, - AccessTools.Method(typeof(InvokeTracker), nameof(InvokeTracker.OnUnityInit))); - LogHook(assembly, type.Name, initMethod.Name); - } - } - } + // doing this seems to fail so Awake hooks are enough + // private static void TryHookRuntimeInits(AssemblyDefinition assembly) + // { + // StaticLogger.Log.LogDebug("Trying to hook all RuntimeInitializeOnLoadMethodAttribute methods"); + // + // var types = assembly.MainModule.GetAllTypes(); + // + // foreach (var type in types) + // { + // // find all methods with RuntimeInitializeOnLoadMethodAttribute + // var initMethods = type.GetMethods().Where(x => + // x.CustomAttributes.Any(a => + // a.AttributeType.FullName == "UnityEngine.RuntimeInitializeOnLoadMethodAttribute")); + // + // foreach (var initMethod in initMethods) + // { + // ILCodeUtils.MethodInvokeHook(assembly, initMethod, + // AccessTools.Method(typeof(InvokeTracker), nameof(InvokeTracker.OnUnityInit))); + // LogHook(assembly, type.Name, initMethod.Name); + // } + // } + // } private static void TryHookAwakes(AssemblyDefinition assembly) { diff --git a/UniTAS/Patcher/Utils/InvokeTracker.cs b/UniTAS/Patcher/Utils/InvokeTracker.cs index 95ee3da8b..7eccd6662 100644 --- a/UniTAS/Patcher/Utils/InvokeTracker.cs +++ b/UniTAS/Patcher/Utils/InvokeTracker.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using UniTAS.Patcher.Interfaces.Invoker; @@ -13,7 +14,7 @@ public static void OnUnityInit() if (_invoked) return; _invoked = true; - StaticLogger.Log.LogDebug("Unity has been initialized"); + StaticLogger.Log.LogDebug($"Unity has been initialized, entry at {new StackTrace()}"); InvokeEventAttributes.Invoke(); } From 7ad90f0c5e4d6d69b3cf714777b41c15759175e0 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 17 Oct 2023 08:28:09 +0100 Subject: [PATCH 087/116] cleaned up target dll property --- .../Preloader/FinalizeSuppressionPatch.cs | 10 +--- .../Patches/Preloader/StaticCtorHeaders.cs | 10 +--- .../Patches/Preloader/UnityInitInvoke.cs | 8 +--- .../Utils/StaticCtorPatchTargetInfo.cs | 31 ------------- UniTAS/Patcher/Utils/TargetPatcherDlls.cs | 46 +++++++++++++++++++ 5 files changed, 49 insertions(+), 56 deletions(-) delete mode 100644 UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs diff --git a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs index 35f70dbdd..e1eec1766 100644 --- a/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/FinalizeSuppressionPatch.cs @@ -1,12 +1,10 @@ using System.Collections.Generic; -using System.IO; using System.Linq; using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; using UniTAS.Patcher.ContainerBindings.GameExecutionControllers; -using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Interfaces; using UniTAS.Patcher.Utils; @@ -14,13 +12,7 @@ namespace UniTAS.Patcher.Patches.Preloader; public class FinalizeSuppressionPatch : PreloadPatcher { - public override IEnumerable TargetDLLs => TargetPatcherDlls.AllDLLs.Where(x => - { - var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); - return fileWithoutExtension == null || - StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || - !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); - }); + public override IEnumerable TargetDLLs => TargetPatcherDlls.AllExcludedDLLs; public override void Patch(ref AssemblyDefinition assembly) { diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index c093af09f..bcd174b25 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -2,14 +2,12 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.IO; using System.Linq; using System.Reflection; using HarmonyLib; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; -using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Interfaces; using UniTAS.Patcher.ManualServices.Trackers; using UniTAS.Patcher.Utils; @@ -20,13 +18,7 @@ namespace UniTAS.Patcher.Patches.Preloader; [SuppressMessage("ReSharper", "UnusedType.Global")] public class StaticCtorHeaders : PreloadPatcher { - public override IEnumerable TargetDLLs => TargetPatcherDlls.AllDLLs.Where(x => - { - var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); - return fileWithoutExtension == null || - StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || - !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); - }); + public override IEnumerable TargetDLLs => TargetPatcherDlls.AllExcludedDLLs; public override void Patch(ref AssemblyDefinition assembly) { diff --git a/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs b/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs index 305cc6277..699c2d353 100644 --- a/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs +++ b/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs @@ -29,13 +29,7 @@ public UnityInitInvoke() : new[] { "UnityEngine.CoreModule.dll", "UnityEngine.dll" }; // add patch target dlls too - targetDLLs = targetDLLs.Concat(TargetPatcherDlls.AllDLLs.Where(x => - { - var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); - return fileWithoutExtension == null || - StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || - !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); - })).Distinct().ToArray(); + targetDLLs = targetDLLs.Concat(TargetPatcherDlls.AllExcludedDLLs).Distinct().ToArray(); TargetDLLs = targetDLLs; _targetClass = GetEntryKey(bepInExConfig, entryPoint, "Type") ?? diff --git a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs b/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs deleted file mode 100644 index a629fdf4b..000000000 --- a/UniTAS/Patcher/Utils/StaticCtorPatchTargetInfo.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace UniTAS.Patcher.Utils; - -public static class StaticCtorPatchTargetInfo -{ - public static string[] AssemblyExclusionsRaw { get; } = - { - "UnityEngine.*", - "UnityEngine", - "Unity.*", - "System.*", - "System", - "netstandard", - "mscorlib", - "Mono.*", - "Mono", - "MonoMod.*", - "BepInEx.*", - "BepInEx", - "MonoMod.*", - "0Harmony", - "HarmonyXInterop", - "StructureMap", - "Newtonsoft.Json" - }; - - public static string[] AssemblyIncludeRaw { get; } = - { - "Unity.InputSystem", - "UnityEngine.InputModule" - }; -} \ No newline at end of file diff --git a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs index b1b050ec3..2169e8c76 100644 --- a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs +++ b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs @@ -2,6 +2,7 @@ using System.IO; using System.Linq; using BepInEx; +using UniTAS.Patcher.Extensions; namespace UniTAS.Patcher.Utils; @@ -10,4 +11,49 @@ public static class TargetPatcherDlls public static IEnumerable AllDLLs => Directory.GetFiles(Paths.ManagedPath, "*.dll", SearchOption.TopDirectoryOnly).Select(Path.GetFileName) .ToArray(); + + // lazy initialization, for some reason directly initializing to the property breaks this + private static IEnumerable _allExcludedDLLs; + + public static IEnumerable AllExcludedDLLs + { + get + { + _allExcludedDLLs ??= AllDLLs.Where(x => + { + var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); + return fileWithoutExtension == null || + AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || + !AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); + }); + return _allExcludedDLLs; + } + } + + private static string[] AssemblyExclusionsRaw { get; } = + { + "UnityEngine.*", + "UnityEngine", + "Unity.*", + "System.*", + "System", + "netstandard", + "mscorlib", + "Mono.*", + "Mono", + "MonoMod.*", + "BepInEx.*", + "BepInEx", + "MonoMod.*", + "0Harmony", + "HarmonyXInterop", + "StructureMap", + "Newtonsoft.Json" + }; + + private static string[] AssemblyIncludeRaw { get; } = + { + "Unity.InputSystem", + "UnityEngine.InputModule" + }; } \ No newline at end of file From 241b38c058ed636a176a1fbf0e13b03aff9fdfd7 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 17 Oct 2023 08:44:15 +0100 Subject: [PATCH 088/116] properly edited the changes reflected --- UniTAS/Patcher/Utils/TargetPatcherDlls.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs index 2169e8c76..43d01714f 100644 --- a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs +++ b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs @@ -41,14 +41,7 @@ public static IEnumerable AllExcludedDLLs "mscorlib", "Mono.*", "Mono", - "MonoMod.*", - "BepInEx.*", - "BepInEx", - "MonoMod.*", - "0Harmony", - "HarmonyXInterop", - "StructureMap", - "Newtonsoft.Json" + "MonoMod.*" }; private static string[] AssemblyIncludeRaw { get; } = From 3d11acad4f9c311e45be450e94a9186e46462c3e Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 17 Oct 2023 08:45:21 +0100 Subject: [PATCH 089/116] include resource manager --- UniTAS/Patcher/Utils/TargetPatcherDlls.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs index 43d01714f..8672c7737 100644 --- a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs +++ b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs @@ -47,6 +47,8 @@ public static IEnumerable AllExcludedDLLs private static string[] AssemblyIncludeRaw { get; } = { "Unity.InputSystem", - "UnityEngine.InputModule" + "UnityEngine.InputModule", + // high level API that manages assets + "Unity.ResourceManager" }; } \ No newline at end of file From 027c6f63f6016b3bd529d9427f47170eac8bb75e Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 17 Oct 2023 08:49:04 +0100 Subject: [PATCH 090/116] applied reflected changes --- UniTAS/Patcher/Utils/TargetPatcherDlls.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs index 8672c7737..4fcb9c107 100644 --- a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs +++ b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs @@ -32,23 +32,16 @@ public static IEnumerable AllExcludedDLLs private static string[] AssemblyExclusionsRaw { get; } = { - "UnityEngine.*", - "UnityEngine", - "Unity.*", + // c# related "System.*", "System", "netstandard", "mscorlib", "Mono.*", - "Mono", - "MonoMod.*" + "Mono" }; private static string[] AssemblyIncludeRaw { get; } = { - "Unity.InputSystem", - "UnityEngine.InputModule", - // high level API that manages assets - "Unity.ResourceManager" }; } \ No newline at end of file From 7caec966bbf776ee3e9fe7d4b2ca96f2636fdf2f Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 17 Oct 2023 08:49:34 +0100 Subject: [PATCH 091/116] fixed to match rebase --- UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs index b07edb662..17e8d7208 100644 --- a/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs +++ b/UniTAS/Patcher/Patches/Preloader/MonoBehaviourPatch.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using HarmonyLib; @@ -16,13 +15,7 @@ namespace UniTAS.Patcher.Patches.Preloader; public class MonoBehaviourPatch : PreloadPatcher { - public override IEnumerable TargetDLLs => TargetPatcherDlls.AllDLLs.Where(x => - { - var fileWithoutExtension = Path.GetFileNameWithoutExtension(x); - return fileWithoutExtension == null || - // StaticCtorPatchTargetInfo.AssemblyIncludeRaw.Any(a => fileWithoutExtension.Like(a)) || - !StaticCtorPatchTargetInfo.AssemblyExclusionsRaw.Any(a => fileWithoutExtension.Like(a)); - }); + public override IEnumerable TargetDLLs => TargetPatcherDlls.AllExcludedDLLs; private const string COLLISION = "UnityEngine.Collision"; private const string COLLISION_2D = "UnityEngine.Collision2D"; From 29e2b8319f9a6a7bd2a26905846ac1ce42f246d5 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 17 Oct 2023 08:57:51 +0100 Subject: [PATCH 092/116] imported missing using for the merged code --- UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs index bcd174b25..f11c28caa 100644 --- a/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs +++ b/UniTAS/Patcher/Patches/Preloader/StaticCtorHeaders.cs @@ -8,6 +8,7 @@ using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Cecil.Rocks; +using UniTAS.Patcher.Extensions; using UniTAS.Patcher.Interfaces; using UniTAS.Patcher.ManualServices.Trackers; using UniTAS.Patcher.Utils; From e19935adc01874fa4b34c48b37ef0ba78868d8b8 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 17 Oct 2023 08:59:42 +0100 Subject: [PATCH 093/116] made AllDLLs private since no point --- UniTAS/Patcher/Utils/TargetPatcherDlls.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs index 4fcb9c107..bcf176407 100644 --- a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs +++ b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs @@ -8,7 +8,7 @@ namespace UniTAS.Patcher.Utils; public static class TargetPatcherDlls { - public static IEnumerable AllDLLs => + private static IEnumerable AllDLLs => Directory.GetFiles(Paths.ManagedPath, "*.dll", SearchOption.TopDirectoryOnly).Select(Path.GetFileName) .ToArray(); From 68a26eb080c7a70dad24822ec88dea8649eedf99 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Tue, 24 Oct 2023 02:30:15 +0100 Subject: [PATCH 094/116] fixed not deep cloning on setting value --- .../UnityFix/SaveScriptableObjectStates.Types.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs index b6feb105f..a5bce47c0 100644 --- a/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs +++ b/UniTAS/Patcher/Implementations/UnityFix/SaveScriptableObjectStates.Types.cs @@ -46,6 +46,7 @@ public StoredState(ScriptableObject scriptableObject, ILogger logger, ITryFreeMa continue; } + StaticLogger.Trace("Field is not unity serializable, resetting on load"); resetFields.Add(field); } @@ -55,9 +56,6 @@ public StoredState(ScriptableObject scriptableObject, ILogger logger, ITryFreeMa public void Load() { - StaticLogger.Log.LogDebug( - $"loading state for type {ScriptableObject.GetType().FullName}, ({ScriptableObject.name}), field count: {_savedFields.Length}, reset field count: {_resetFields.Length}"); - if (ScriptableObject == null) { _logger.LogError("ScriptableObject is null, this should not happen"); @@ -71,7 +69,6 @@ public void Load() foreach (var resetField in _resetFields) { - StaticLogger.Log.LogDebug($"Resetting field {resetField.Name}"); _freeMalloc?.TryFree(ScriptableObject, resetField); resetField.SetValue(ScriptableObject, null); } @@ -110,12 +107,12 @@ public FieldData(FieldInfo fieldInfo, ScriptableObject instance, ILogger logger, public void Load() { - StaticLogger.Log.LogDebug($"Loading field {_saveField.Name}, value is {_value}"); - // try to free pointer if it's a pointer _freeMalloc?.TryFree(_instance, _saveField); - _saveField.SetValue(_instance, _value.IsLeft ? _value.Left : _value.Right); + // additional one to make it not use the stored value + var value = DeepCopy.MakeDeepCopy(_value.IsLeft ? _value.Left : _value.Right); + _saveField.SetValue(_instance, value); } } } \ No newline at end of file From cccaa222dcee58f806826a6278e9ff33a623543d Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 27 Oct 2023 02:19:09 +0100 Subject: [PATCH 095/116] made debug print much more advanced --- UniTAS/Patcher/Utils/DebugHelp.cs | 99 ++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 3 deletions(-) diff --git a/UniTAS/Patcher/Utils/DebugHelp.cs b/UniTAS/Patcher/Utils/DebugHelp.cs index 4cf30163d..df685126d 100644 --- a/UniTAS/Patcher/Utils/DebugHelp.cs +++ b/UniTAS/Patcher/Utils/DebugHelp.cs @@ -1,19 +1,112 @@ +using System; +using System.Collections.Generic; using HarmonyLib; +using UnityEngine; +using Object = UnityEngine.Object; namespace UniTAS.Patcher.Utils; public static class DebugHelp { public static string PrintClass(object obj) + { + var indent = 0; + return PrintClass(obj, ref indent, new()); + } + + private static string PrintClass(object obj, ref int indent, List foundReferences, + bool ignoreInitialIndent = false) { var type = obj.GetType(); - var fields = AccessTools.GetDeclaredFields(type); - var str = $"{type.Name} " + "{\n"; + var str = $"{(ignoreInitialIndent ? "" : IndentString(indent))}{type.Name} {{\n"; + indent++; + + if (type.IsClass) + { + foundReferences.Add(obj); + } + + var fields = AccessTools.GetDeclaredFields(obj.GetType()); + foreach (var field in fields) { - str += $" {field.Name}: {field.GetValue(obj)},\n"; + if (field.IsStatic || field.IsLiteral) continue; + + str += $"{IndentString(indent)}{field.Name}: "; + + var value = field.GetValue(obj); + + // circular reference + if (foundReferences.Contains(value)) + { + str += "...,\n"; + continue; + } + + var fieldType = field.FieldType; + + if (value is null) + { + str += "null,\n"; + continue; + } + + // direct use cases + if (fieldType.IsPrimitive || fieldType.IsEnum || + value is Object and not MonoBehaviour and not ScriptableObject) + { + str += $"{value},\n"; + continue; + } + + if (value is string) + { + str += $"\"{value}\",\n"; + continue; + } + + if (value is Array array) + { + str += "["; + + if (array.Length == 0) + { + str += "],\n"; + continue; + } + + str += "\n"; + + indent++; + + foreach (var item in array) + { + str += $"{PrintClass(item, ref indent, foundReferences)}\n"; + } + + indent--; + str += $"{IndentString(indent)}],\n"; + continue; + } + + var ns = fieldType.Namespace; + if (ns == "System" || (ns?.StartsWith("System.") ?? false)) + { + str += $"{value},\n"; + continue; + } + + // fallback + str += $"{PrintClass(value, ref indent, foundReferences, true)},\n"; } + indent--; + str += $"{IndentString(indent)}}}"; return str; } + + private static string IndentString(int indent) + { + return new(' ', indent * 2); + } } \ No newline at end of file From 4cc48aae646f9b68c4fc762580e82064b7eda5ea Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 27 Oct 2023 22:39:31 +0100 Subject: [PATCH 096/116] removed ns check and directly printing cuz c# std lib sucks --- UniTAS/Patcher/Utils/DebugHelp.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/UniTAS/Patcher/Utils/DebugHelp.cs b/UniTAS/Patcher/Utils/DebugHelp.cs index df685126d..ae1aa5398 100644 --- a/UniTAS/Patcher/Utils/DebugHelp.cs +++ b/UniTAS/Patcher/Utils/DebugHelp.cs @@ -89,13 +89,6 @@ private static string PrintClass(object obj, ref int indent, List foundR continue; } - var ns = fieldType.Namespace; - if (ns == "System" || (ns?.StartsWith("System.") ?? false)) - { - str += $"{value},\n"; - continue; - } - // fallback str += $"{PrintClass(value, ref indent, foundReferences, true)},\n"; } From 2d7be90dbe707897de0312b1a9e65276e64cfbb5 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 27 Oct 2023 23:41:10 +0100 Subject: [PATCH 097/116] fixed handling IEnumerable and system namespace cloning --- UniTAS/Patcher/Utils/DeepCopy.cs | 109 ++++++++++++++++--------------- 1 file changed, 55 insertions(+), 54 deletions(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 6374d5a77..1b28e95fe 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -1,5 +1,4 @@ using System; -using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -92,7 +91,7 @@ private static object MakeDeepCopy(object source, Func - i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); - } - - var resultTypeGenericArgument = resultTypeInterface.GetGenericArguments()[0]; - var iEnumerableType = typeof(IEnumerable<>).MakeGenericType(resultTypeGenericArgument); - - var ctor = AccessTools.Constructor(type, new[] { iEnumerableType }); - if (ctor != null) - { - var tempResultList = - (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(resultTypeGenericArgument)); - foreach (var element in sourceCollection) - { - var newElement = MakeDeepCopy(element, processor, pathRoot, - foundReferences, newReferences, ref id); - tempResultList.Add(newElement); - } - - var addableResult = ctor.Invoke(new object[] { tempResultList }); - - _makeDeepCopyRecursionDepth--; - newReferences.Add(newRefId, addableResult); - StaticLogger.Trace("MakeDeepCopy, returning IEnumerable"); - return addableResult; - } - - StaticLogger.Trace("MakeDeepCopy, IEnumerable has no constructor with IEnumerable argument"); - } - - var ns = type.Namespace; - if (ns == "System" || (ns?.StartsWith("System.") ?? false)) - { - _makeDeepCopyRecursionDepth--; - StaticLogger.Trace($"MakeDeepCopy, returning system type, namespace: {ns}"); - return source; - } + // if (typeof(IEnumerable).IsAssignableFrom(type)) + // { + // StaticLogger.Trace("MakeDeepCopy, copying IEnumerable"); + // var sourceCollection = (IEnumerable)source; + // foundReferences.Add(id, source); + // var newRefId = id; + // id++; + // + // Type resultTypeInterface; + // if (type.IsInterface) + // { + // resultTypeInterface = type; + // } + // else + // { + // resultTypeInterface = type.GetInterfaces().First(i => + // i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>)); + // } + // + // var resultTypeGenericArgument = resultTypeInterface.GetGenericArguments()[0]; + // var iEnumerableType = typeof(IEnumerable<>).MakeGenericType(resultTypeGenericArgument); + // + // var ctor = AccessTools.Constructor(type, new[] { iEnumerableType }); + // if (ctor != null) + // { + // var tempResultList = + // (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(resultTypeGenericArgument)); + // foreach (var element in sourceCollection) + // { + // var newElement = MakeDeepCopy(element, processor, pathRoot, + // foundReferences, newReferences, ref id, ref a); + // tempResultList.Add(newElement); + // } + // + // var addableResult = ctor.Invoke(new object[] { tempResultList }); + // + // _makeDeepCopyRecursionDepth--; + // newReferences.Add(newRefId, addableResult); + // StaticLogger.Trace("MakeDeepCopy, returning IEnumerable"); + // if (a > 0) a++; + // return addableResult; + // } + // + // StaticLogger.Trace("MakeDeepCopy, IEnumerable has no constructor with IEnumerable argument"); + // } + + // var ns = type.Namespace; + // if (ns == "System" || (ns?.StartsWith("System.") ?? false)) + // { + // _makeDeepCopyRecursionDepth--; + // StaticLogger.Trace($"MakeDeepCopy, returning system type, namespace: {ns}"); + // if (a > 0) a++; + // return source; + // } StaticLogger.Trace("MakeDeepCopy, creating new instance and copying fields"); var result = AccessTools.CreateInstance(type); From d50cbf1438dbe19ddafe3c59408a4ea8d2294652 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 28 Oct 2023 06:53:13 +0100 Subject: [PATCH 098/116] made command shorter --- .../Implementations/GUI/TerminalCommands/RunUnitTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Implementations/GUI/TerminalCommands/RunUnitTests.cs b/UniTAS/Patcher/Implementations/GUI/TerminalCommands/RunUnitTests.cs index 25ad89dce..2dc1ab741 100644 --- a/UniTAS/Patcher/Implementations/GUI/TerminalCommands/RunUnitTests.cs +++ b/UniTAS/Patcher/Implementations/GUI/TerminalCommands/RunUnitTests.cs @@ -8,7 +8,7 @@ namespace UniTAS.Patcher.Implementations.GUI.TerminalCommands; public class RunUnitTests : TerminalEntry { - public override string Command => "run_tests"; + public override string Command => "test"; public override string Description => "Runs defined runtime unit tests with current game. Arg 0 (uint) (optional): how many times to run the tests"; From 18a8046878a742327f5bfe0143a913174b68df66 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Sat, 28 Oct 2023 18:35:48 +0100 Subject: [PATCH 099/116] init ScriptableObject instance properly --- UniTAS/Patcher/Utils/DeepCopy.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/DeepCopy.cs b/UniTAS/Patcher/Utils/DeepCopy.cs index 1b28e95fe..8d655a27a 100644 --- a/UniTAS/Patcher/Utils/DeepCopy.cs +++ b/UniTAS/Patcher/Utils/DeepCopy.cs @@ -185,7 +185,9 @@ private static object MakeDeepCopy(object source, Func Date: Sat, 28 Oct 2023 19:16:52 +0100 Subject: [PATCH 100/116] stop all audio sources on restart --- .../UnityFix/ClearAudioOnRestart.cs | 19 +++++++++++++++++++ UniTAS/Patcher/Utils/ObjectUtils.cs | 11 +++++++++++ 2 files changed, 30 insertions(+) create mode 100644 UniTAS/Patcher/Implementations/UnityFix/ClearAudioOnRestart.cs create mode 100644 UniTAS/Patcher/Utils/ObjectUtils.cs diff --git a/UniTAS/Patcher/Implementations/UnityFix/ClearAudioOnRestart.cs b/UniTAS/Patcher/Implementations/UnityFix/ClearAudioOnRestart.cs new file mode 100644 index 000000000..d4c129829 --- /dev/null +++ b/UniTAS/Patcher/Implementations/UnityFix/ClearAudioOnRestart.cs @@ -0,0 +1,19 @@ +using UniTAS.Patcher.Interfaces.DependencyInjection; +using UniTAS.Patcher.Interfaces.Events.SoftRestart; +using UniTAS.Patcher.Utils; +using UnityEngine; + +namespace UniTAS.Patcher.Implementations.UnityFix; + +[Singleton] +public class ClearAudioOnRestart : IOnPreGameRestart +{ + public void OnPreGameRestart() + { + var audioSources = ObjectUtils.FindObjectsOfType(); + foreach (var audioSource in audioSources) + { + audioSource.Stop(); + } + } +} \ No newline at end of file diff --git a/UniTAS/Patcher/Utils/ObjectUtils.cs b/UniTAS/Patcher/Utils/ObjectUtils.cs new file mode 100644 index 000000000..c41f00ed6 --- /dev/null +++ b/UniTAS/Patcher/Utils/ObjectUtils.cs @@ -0,0 +1,11 @@ +using UnityEngine; + +namespace UniTAS.Patcher.Utils; + +public static class ObjectUtils +{ + public static T[] FindObjectsOfType() + { + return Object.FindObjectsOfType(typeof(T)) as T[]; + } +} \ No newline at end of file From 68931c9b84605f34ef7705e94b494d2b74816a57 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 3 Nov 2023 14:01:12 +0000 Subject: [PATCH 101/116] added extra logging stuff --- UniTAS/Patcher/Entry.cs | 2 +- UniTAS/Patcher/Utils/LoggingUtils.cs | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/UniTAS/Patcher/Entry.cs b/UniTAS/Patcher/Entry.cs index 390ec85dd..18bd7601f 100644 --- a/UniTAS/Patcher/Entry.cs +++ b/UniTAS/Patcher/Entry.cs @@ -25,7 +25,7 @@ public static class Entry [SuppressMessage("ReSharper", "UnusedMember.Global")] public static void Initialize() { - LoggingUtils.InitDiskLogger(); + LoggingUtils.Init(); StaticLogger.Log.LogInfo("Initializing UniTAS"); BepInExUtils.GenerateMissingDirs(); diff --git a/UniTAS/Patcher/Utils/LoggingUtils.cs b/UniTAS/Patcher/Utils/LoggingUtils.cs index d83b3cd3f..e4988b131 100644 --- a/UniTAS/Patcher/Utils/LoggingUtils.cs +++ b/UniTAS/Patcher/Utils/LoggingUtils.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Threading; using BepInEx; @@ -7,12 +8,14 @@ namespace UniTAS.Patcher.Utils; public static class LoggingUtils { - public static void InitDiskLogger() + public static void Init() { var diskLogger = new DiskLogger(); if (!diskLogger.Enabled) return; Logger.Listeners.Add(diskLogger); + + AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionLog; } // separate disk logging @@ -23,8 +26,13 @@ public class DiskLogger : ILogListener public bool Enabled { get; } = true; + public static DiskLogger Instance { get; private set; } + public DiskLogger() { + if (Instance != null) return; + Instance = this; + if (!Utility.TryOpenFileStream(Path.Combine(Paths.BepInExRootPath, "UniTAS.log"), FileMode.Create, out var fileStream, FileAccess.Write)) { @@ -44,9 +52,20 @@ public void Dispose() _logWriter?.Dispose(); } + public void Flush() + { + _logWriter?.Flush(); + } + public void LogEvent(object sender, LogEventArgs eventArgs) { _logWriter.WriteLine(eventArgs.ToString()); } } + + private static void UnhandledExceptionLog(object sender, UnhandledExceptionEventArgs e) + { + StaticLogger.Log.LogFatal(e.ExceptionObject); + DiskLogger.Instance.Flush(); + } } \ No newline at end of file From af03d2e655532fe43ee047c3ed1d329815b5dac1 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 3 Nov 2023 14:15:58 +0000 Subject: [PATCH 102/116] changed order of event invoke to make sense --- UniTAS/Patcher/Implementations/Movie/MovieRunner.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Implementations/Movie/MovieRunner.cs b/UniTAS/Patcher/Implementations/Movie/MovieRunner.cs index 7efc73050..48ad8a3fd 100644 --- a/UniTAS/Patcher/Implementations/Movie/MovieRunner.cs +++ b/UniTAS/Patcher/Implementations/Movie/MovieRunner.cs @@ -147,6 +147,8 @@ private void RunUpdateEvents(bool fixedUpdate) private void MovieRunningStatusChange(bool running) { + MovieEnd = !running; + if (running) { OnMovieStart?.Invoke(); @@ -156,7 +158,6 @@ private void MovieRunningStatusChange(bool running) OnMovieEnd?.Invoke(); } - MovieEnd = !running; foreach (var onMovieRunningStatusChange in _onMovieRunningStatusChange) { onMovieRunningStatusChange.OnMovieRunningStatusChange(running); From 687647365756a2eef5ad2efa1617ebe6137e9c00 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 3 Nov 2023 14:30:59 +0000 Subject: [PATCH 103/116] changed API for TAS devices --- .../NewInputSystem/InputSystemOverride.cs | 67 +++++++++++-------- .../NewInputSystem/KeyboardDeviceOverride.cs | 10 +-- .../NewInputSystem/MouseDeviceOverride.cs | 11 +-- .../IInputOverrideDevice.cs | 8 +-- 4 files changed, 45 insertions(+), 51 deletions(-) diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs index b157d4801..1836bd9a6 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs @@ -1,9 +1,14 @@ +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Interfaces.InputSystemOverride; +using UniTAS.Patcher.Services; using UniTAS.Patcher.Services.InputSystemOverride; using UniTAS.Patcher.Services.Logging; +using UniTAS.Patcher.Services.Movie; using UniTAS.Patcher.Services.NewInputSystem; +using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; using UniTAS.Patcher.Services.UnityEvents; using UnityEngine.InputSystem; @@ -11,29 +16,32 @@ namespace UniTAS.Patcher.Implementations.NewInputSystem; [Singleton] [ForceInstantiate] -public class InputSystemOverride : IInputSystemOverride +public class InputSystemOverride : IInputSystemOverride, IInputSystemAddedDeviceTracker { private readonly IInputOverrideDevice[] _devices; - private InputDevice[] _restoreDevices; private readonly ILogger _logger; + private readonly IMovieRunner _movieRunner; private readonly bool _hasInputSystem; private bool _override; [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")] public InputSystemOverride(ILogger logger, IInputOverrideDevice[] devices, - INewInputSystemExists newInputSystemExists, IUpdateEvents updateEvents) + INewInputSystemExists newInputSystemExists, IUpdateEvents updateEvents, IMovieRunner movieRunner, + IMovieRunnerEvents movieRunnerEvents, IGameRestart gameRestart) { - _logger = logger; - _devices = devices; - _hasInputSystem = newInputSystemExists.HasInputSystem; - _logger.LogMessage($"Has unity new input system: {_hasInputSystem}"); + logger.LogMessage($"Has unity new input system: {_hasInputSystem}"); if (!_hasInputSystem) return; + _logger = logger; + _devices = devices; + _movieRunner = movieRunner; updateEvents.OnInputUpdateActual += UpdateDevices; + movieRunnerEvents.OnMovieStart += OnMovieStart; + movieRunnerEvents.OnMovieEnd += OnMovieEnd; } public bool Override @@ -41,35 +49,28 @@ public bool Override set { if (!_hasInputSystem) return; - _override = value; - if (_override) + + if (value) { _logger.LogDebug("Adding TAS devices to InputSystem"); - _restoreDevices = InputSystem.devices.ToArray(); - RemoveAndFlushAllDevices(); - foreach (var device in _devices) { - device.DeviceAdded(); + InputSystem.AddDevice(device.Device); } _logger.LogDebug("Added TAS devices to InputSystem"); + _logger.LogDebug( + $"Connected devices:\n{string.Join("\n", InputSystem.devices.Select(x => $"name: {x.name}, type: {x.GetType().FullName}").ToArray())}"); } else { - _logger.LogDebug("Removing TAS devices from InputSystem"); - - RemoveAndFlushAllDevices(); + _logger.LogDebug($"Removing TAS devices from InputSystem, {new StackTrace()}"); - // restore devices - if (_restoreDevices != null) + foreach (var device in _devices) { - foreach (var device in _restoreDevices) - { - InputSystem.AddDevice(device); - } + InputSystem.RemoveDevice(device.Device); } _logger.LogDebug("Removed TAS devices from InputSystem"); @@ -79,7 +80,7 @@ public bool Override private void UpdateDevices(bool fixedUpdate, bool newInputSystemUpdate) { - if (!_override || !newInputSystemUpdate) return; + if (!newInputSystemUpdate || !_override) return; foreach (var device in _devices) { @@ -87,13 +88,21 @@ private void UpdateDevices(bool fixedUpdate, bool newInputSystemUpdate) } } - private static void RemoveAndFlushAllDevices() + public void AddDevice(InputDevice device) { - while (InputSystem.devices.Count > 0) - { - InputSystem.RemoveDevice(InputSystem.devices[0]); - } + // if (_movieRunner.MovieEnd) return; + // + // _logger.LogDebug($"Adding device on movie end ({device.name} - {device.GetType().Name})"); + // _deviceToAddOnMovieEnd.Add(device); + } - InputSystem.FlushDisconnectedDevices(); + private void OnMovieStart() + { + Override = true; + } + + private void OnMovieEnd() + { + Override = false; } } \ No newline at end of file diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs index f9314bb05..bd379cd7b 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs @@ -11,9 +11,8 @@ namespace UniTAS.Patcher.Implementations.NewInputSystem; [Singleton] public class KeyboardDeviceOverride : IInputOverrideDevice { - private Keyboard _keyboard; - private readonly IKeyboardStateEnvNewSystem _keyboardStateEnvNewSystem; + public InputDevice Device { get; } = new TASKeyboard(); public KeyboardDeviceOverride(IKeyboardStateEnvNewSystem keyboardStateEnvNewSystem) { @@ -34,11 +33,6 @@ public void Update() state.Set(heldKey.Key, true); } - InputSystem.QueueStateEvent(_keyboard, state); - } - - public void DeviceAdded() - { - _keyboard = InputSystem.AddDevice(); + InputSystem.QueueStateEvent(Device, state); } } \ No newline at end of file diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/MouseDeviceOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/MouseDeviceOverride.cs index fcc2c9dc2..ccd39b81b 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/MouseDeviceOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/MouseDeviceOverride.cs @@ -12,8 +12,6 @@ namespace UniTAS.Patcher.Implementations.NewInputSystem; [Singleton] public class MouseDeviceOverride : IInputOverrideDevice { - private Mouse _mouse; - private readonly IMouseStateEnvNewSystem _mouseStateEnvNewSystem; public MouseDeviceOverride(IMouseStateEnvNewSystem mouseStateEnvNewSystem) @@ -21,6 +19,8 @@ public MouseDeviceOverride(IMouseStateEnvNewSystem mouseStateEnvNewSystem) _mouseStateEnvNewSystem = mouseStateEnvNewSystem; } + public InputDevice Device { get; } = new TASMouse(); + [InputControlLayout(stateType = typeof(MouseState), isGenericTypeOfDevice = true)] [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")] private class TASMouse : Mouse @@ -52,11 +52,6 @@ public void Update() scroll = _mouseStateEnvNewSystem.Scroll }; - InputSystem.QueueStateEvent(_mouse, state); - } - - public void DeviceAdded() - { - _mouse = InputSystem.AddDevice(); + InputSystem.QueueStateEvent(Device, state); } } \ No newline at end of file diff --git a/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs b/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs index b70cc8207..7f609edf6 100644 --- a/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs +++ b/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs @@ -1,8 +1,7 @@ -using UniTAS.Patcher.Interfaces.DependencyInjection; +using UnityEngine.InputSystem; namespace UniTAS.Patcher.Interfaces.InputSystemOverride; -[RegisterAll] public interface IInputOverrideDevice { /// @@ -10,8 +9,5 @@ public interface IInputOverrideDevice /// void Update(); - /// - /// Called when the device is to be added to the input system - /// - void DeviceAdded(); + InputDevice Device { get; } } \ No newline at end of file From 927e139db9b877f67224c711775d20325c897ef7 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 3 Nov 2023 14:44:57 +0000 Subject: [PATCH 104/116] fixed exception from throwing --- .../NewInputSystem/InputSystemOverride.cs | 11 ++++------- .../NewInputSystem/KeyboardDeviceOverride.cs | 15 +++++++++++++-- .../NewInputSystem/MouseDeviceOverride.cs | 15 +++++++++++++-- .../InputSystemOverride/IInputOverrideDevice.cs | 12 +++++++++--- 4 files changed, 39 insertions(+), 14 deletions(-) diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs index 1836bd9a6..c6ac0f9be 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs @@ -8,7 +8,6 @@ using UniTAS.Patcher.Services.Logging; using UniTAS.Patcher.Services.Movie; using UniTAS.Patcher.Services.NewInputSystem; -using UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; using UniTAS.Patcher.Services.UnityEvents; using UnityEngine.InputSystem; @@ -21,15 +20,14 @@ public class InputSystemOverride : IInputSystemOverride, IInputSystemAddedDevice private readonly IInputOverrideDevice[] _devices; private readonly ILogger _logger; - private readonly IMovieRunner _movieRunner; private readonly bool _hasInputSystem; private bool _override; [SuppressMessage("ReSharper", "ConditionIsAlwaysTrueOrFalse")] public InputSystemOverride(ILogger logger, IInputOverrideDevice[] devices, - INewInputSystemExists newInputSystemExists, IUpdateEvents updateEvents, IMovieRunner movieRunner, - IMovieRunnerEvents movieRunnerEvents, IGameRestart gameRestart) + INewInputSystemExists newInputSystemExists, IUpdateEvents updateEvents, IMovieRunnerEvents movieRunnerEvents, + IGameRestart gameRestart) { _hasInputSystem = newInputSystemExists.HasInputSystem; logger.LogMessage($"Has unity new input system: {_hasInputSystem}"); @@ -38,7 +36,6 @@ public InputSystemOverride(ILogger logger, IInputOverrideDevice[] devices, _logger = logger; _devices = devices; - _movieRunner = movieRunner; updateEvents.OnInputUpdateActual += UpdateDevices; movieRunnerEvents.OnMovieStart += OnMovieStart; movieRunnerEvents.OnMovieEnd += OnMovieEnd; @@ -57,7 +54,7 @@ public bool Override foreach (var device in _devices) { - InputSystem.AddDevice(device.Device); + device.AddDevice(); } _logger.LogDebug("Added TAS devices to InputSystem"); @@ -70,7 +67,7 @@ public bool Override foreach (var device in _devices) { - InputSystem.RemoveDevice(device.Device); + device.RemoveDevice(); } _logger.LogDebug("Removed TAS devices from InputSystem"); diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs index bd379cd7b..8cce8dbd6 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/KeyboardDeviceOverride.cs @@ -12,7 +12,7 @@ namespace UniTAS.Patcher.Implementations.NewInputSystem; public class KeyboardDeviceOverride : IInputOverrideDevice { private readonly IKeyboardStateEnvNewSystem _keyboardStateEnvNewSystem; - public InputDevice Device { get; } = new TASKeyboard(); + private TASKeyboard _device; public KeyboardDeviceOverride(IKeyboardStateEnvNewSystem keyboardStateEnvNewSystem) { @@ -33,6 +33,17 @@ public void Update() state.Set(heldKey.Key, true); } - InputSystem.QueueStateEvent(Device, state); + InputSystem.QueueStateEvent(_device, state); + } + + public void AddDevice() + { + _device = InputSystem.AddDevice(); + } + + public void RemoveDevice() + { + InputSystem.RemoveDevice(_device); + _device = null; } } \ No newline at end of file diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/MouseDeviceOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/MouseDeviceOverride.cs index ccd39b81b..90d05d596 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/MouseDeviceOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/MouseDeviceOverride.cs @@ -19,7 +19,7 @@ public MouseDeviceOverride(IMouseStateEnvNewSystem mouseStateEnvNewSystem) _mouseStateEnvNewSystem = mouseStateEnvNewSystem; } - public InputDevice Device { get; } = new TASMouse(); + private TASMouse _device; [InputControlLayout(stateType = typeof(MouseState), isGenericTypeOfDevice = true)] [SuppressMessage("ReSharper", "ClassNeverInstantiated.Local")] @@ -52,6 +52,17 @@ public void Update() scroll = _mouseStateEnvNewSystem.Scroll }; - InputSystem.QueueStateEvent(Device, state); + InputSystem.QueueStateEvent(_device, state); + } + + public void AddDevice() + { + _device = InputSystem.AddDevice(); + } + + public void RemoveDevice() + { + InputSystem.RemoveDevice(_device); + _device = null; } } \ No newline at end of file diff --git a/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs b/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs index 7f609edf6..6281cdffb 100644 --- a/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs +++ b/UniTAS/Patcher/Interfaces/InputSystemOverride/IInputOverrideDevice.cs @@ -1,5 +1,3 @@ -using UnityEngine.InputSystem; - namespace UniTAS.Patcher.Interfaces.InputSystemOverride; public interface IInputOverrideDevice @@ -9,5 +7,13 @@ public interface IInputOverrideDevice /// void Update(); - InputDevice Device { get; } + /// + /// Called when the device is to be added to the input system + /// + void AddDevice(); + + /// + /// Called when device is to be removed from the input system + /// + void RemoveDevice(); } \ No newline at end of file From 7b5b21a0f39f6cc0f0906bd7f6fd3f60a5a7836c Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 3 Nov 2023 14:45:20 +0000 Subject: [PATCH 105/116] removal of a probably useless API --- .../NewInputSystem/InputSystemOverride.cs | 10 +--------- .../UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs | 8 -------- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs index c6ac0f9be..f128609bf 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs @@ -15,7 +15,7 @@ namespace UniTAS.Patcher.Implementations.NewInputSystem; [Singleton] [ForceInstantiate] -public class InputSystemOverride : IInputSystemOverride, IInputSystemAddedDeviceTracker +public class InputSystemOverride : IInputSystemOverride { private readonly IInputOverrideDevice[] _devices; @@ -85,14 +85,6 @@ private void UpdateDevices(bool fixedUpdate, bool newInputSystemUpdate) } } - public void AddDevice(InputDevice device) - { - // if (_movieRunner.MovieEnd) return; - // - // _logger.LogDebug($"Adding device on movie end ({device.name} - {device.GetType().Name})"); - // _deviceToAddOnMovieEnd.Add(device); - } - private void OnMovieStart() { Override = true; diff --git a/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs b/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs deleted file mode 100644 index 7bc0df5bd..000000000 --- a/UniTAS/Patcher/Services/Trackers/UpdateTrackInfo/IInputSystemAddedDeviceTracker.cs +++ /dev/null @@ -1,8 +0,0 @@ -using UnityEngine.InputSystem; - -namespace UniTAS.Patcher.Services.Trackers.UpdateTrackInfo; - -public interface IInputSystemAddedDeviceTracker -{ - void AddDevice(InputDevice device); -} \ No newline at end of file From 774a936be7105789353c33ee30e8d07669846c5f Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 3 Nov 2023 14:54:38 +0000 Subject: [PATCH 106/116] removed annoying stack trace dump --- .../Implementations/NewInputSystem/InputSystemOverride.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs b/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs index f128609bf..bfc9d8233 100644 --- a/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs +++ b/UniTAS/Patcher/Implementations/NewInputSystem/InputSystemOverride.cs @@ -1,4 +1,3 @@ -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using UniTAS.Patcher.Interfaces.DependencyInjection; @@ -63,7 +62,7 @@ public bool Override } else { - _logger.LogDebug($"Removing TAS devices from InputSystem, {new StackTrace()}"); + _logger.LogDebug($"Removing TAS devices from InputSystem"); foreach (var device in _devices) { From f40ca15edc43c7ff00de9b1cfc214d0a3a87de57 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 3 Nov 2023 15:17:30 +0000 Subject: [PATCH 107/116] prevent crashing somehow --- .../Patcher/Implementations/StaticFieldStorage.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/UniTAS/Patcher/Implementations/StaticFieldStorage.cs b/UniTAS/Patcher/Implementations/StaticFieldStorage.cs index 553b24281..7dcdb3b7d 100644 --- a/UniTAS/Patcher/Implementations/StaticFieldStorage.cs +++ b/UniTAS/Patcher/Implementations/StaticFieldStorage.cs @@ -1,4 +1,5 @@ using System; +using System.Threading; using UniTAS.Patcher.Interfaces.DependencyInjection; using UniTAS.Patcher.Models.DependencyInjection; using UniTAS.Patcher.Services; @@ -14,11 +15,13 @@ public class StaticFieldStorage : IStaticFieldManipulator { private readonly ILogger _logger; private readonly IClassStaticInfoTracker _classStaticInfoTracker; + private readonly ITryFreeMalloc _freeMalloc; - public StaticFieldStorage(ILogger logger, IClassStaticInfoTracker classStaticInfoTracker) + public StaticFieldStorage(ILogger logger, IClassStaticInfoTracker classStaticInfoTracker, ITryFreeMalloc freeMalloc) { _logger = logger; _classStaticInfoTracker = classStaticInfoTracker; + _freeMalloc = freeMalloc; } public void ResetStaticFields() @@ -40,6 +43,7 @@ public void ResetStaticFields() disposable.Dispose(); } + _freeMalloc?.TryFree(null, field); field.SetValue(null, null); } @@ -47,9 +51,9 @@ public void ResetStaticFields() GC.Collect(); GC.WaitForPendingFinalizers(); - _logger.LogDebug("calling static constructors"); - // ReSharper disable once ForCanBeConvertedToForeach - for (var i = 0; i < _classStaticInfoTracker.StaticCtorInvokeOrder.Count; i++) + var count = _classStaticInfoTracker.StaticCtorInvokeOrder.Count; + _logger.LogDebug($"calling {count} static constructors"); + for (var i = 0; i < count; i++) { var staticCtorType = _classStaticInfoTracker.StaticCtorInvokeOrder[i]; var cctor = staticCtorType.TypeInitializer; @@ -63,6 +67,9 @@ public void ResetStaticFields() { _logger.LogDebug($"Exception thrown while calling static constructor: {e}"); } + + // ik calling static ctors in the first place is illegal as fk but why does this prevent crashing?????? + Thread.Sleep(1); } } } \ No newline at end of file From f44ed5c73de8c47ee05f9ee7845eb0cd7e714ac5 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 3 Nov 2023 15:18:07 +0000 Subject: [PATCH 108/116] updated --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9ad68124..870122b7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### General - Fixed TAS rendering popping up command prompt probably +- Fixed music overlaying on each other every restart ### Compatibility From 861f58ba54b90c2a07ad3e99c189b825e900f3fb Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 9 Nov 2023 13:24:48 +0000 Subject: [PATCH 109/116] fixed some weird code --- UniTAS/Patcher/Utils/DebugHelp.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/UniTAS/Patcher/Utils/DebugHelp.cs b/UniTAS/Patcher/Utils/DebugHelp.cs index ae1aa5398..c5db3adfa 100644 --- a/UniTAS/Patcher/Utils/DebugHelp.cs +++ b/UniTAS/Patcher/Utils/DebugHelp.cs @@ -26,7 +26,7 @@ private static string PrintClass(object obj, ref int indent, List foundR foundReferences.Add(obj); } - var fields = AccessTools.GetDeclaredFields(obj.GetType()); + var fields = AccessTools.GetDeclaredFields(type); foreach (var field in fields) { @@ -43,14 +43,14 @@ private static string PrintClass(object obj, ref int indent, List foundR continue; } - var fieldType = field.FieldType; - if (value is null) { str += "null,\n"; continue; } + var fieldType = field.FieldType; + // direct use cases if (fieldType.IsPrimitive || fieldType.IsEnum || value is Object and not MonoBehaviour and not ScriptableObject) From cb277dfb47fbc1a3963bd5011b17f3fb0a13186a Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 16 Nov 2023 19:26:33 +0000 Subject: [PATCH 110/116] fixed crashing trying to read pointer value --- UniTAS/Patcher/Utils/DebugHelp.cs | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/DebugHelp.cs b/UniTAS/Patcher/Utils/DebugHelp.cs index c5db3adfa..b0875d5a5 100644 --- a/UniTAS/Patcher/Utils/DebugHelp.cs +++ b/UniTAS/Patcher/Utils/DebugHelp.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using HarmonyLib; using UnityEngine; using Object = UnityEngine.Object; @@ -17,6 +18,11 @@ public static string PrintClass(object obj) private static string PrintClass(object obj, ref int indent, List foundReferences, bool ignoreInitialIndent = false) { + if (obj == null) + { + return $"{(ignoreInitialIndent ? "" : IndentString(indent))}null"; + } + var type = obj.GetType(); var str = $"{(ignoreInitialIndent ? "" : IndentString(indent))}{type.Name} {{\n"; indent++; @@ -30,7 +36,10 @@ private static string PrintClass(object obj, ref int indent, List foundR foreach (var field in fields) { - if (field.IsStatic || field.IsLiteral) continue; + if (field.IsStatic || field.IsLiteral) + { + continue; + } str += $"{IndentString(indent)}{field.Name}: "; @@ -52,6 +61,17 @@ private static string PrintClass(object obj, ref int indent, List foundR var fieldType = field.FieldType; // direct use cases + if (fieldType.IsPointer) + { + unsafe + { + var rawValue = (IntPtr)Pointer.Unbox(value); + str += $"ptr(0x{rawValue.ToInt64():X})"; + } + + continue; + } + if (fieldType.IsPrimitive || fieldType.IsEnum || value is Object and not MonoBehaviour and not ScriptableObject) { From 4cd9f5ab94fc223497e66f19591c125dcd71f1fb Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 16 Nov 2023 19:32:33 +0000 Subject: [PATCH 111/116] fixed array formatting --- UniTAS/Patcher/Utils/DebugHelp.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/DebugHelp.cs b/UniTAS/Patcher/Utils/DebugHelp.cs index b0875d5a5..440b2eac4 100644 --- a/UniTAS/Patcher/Utils/DebugHelp.cs +++ b/UniTAS/Patcher/Utils/DebugHelp.cs @@ -101,7 +101,7 @@ private static string PrintClass(object obj, ref int indent, List foundR foreach (var item in array) { - str += $"{PrintClass(item, ref indent, foundReferences)}\n"; + str += $"{PrintClass(item, ref indent, foundReferences)},\n"; } indent--; From f642757af5998ee7e682090e2e6a9d20dc780755 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Thu, 16 Nov 2023 20:30:47 +0000 Subject: [PATCH 112/116] fixed some bits related to circular references --- UniTAS/Patcher/Utils/DebugHelp.cs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/UniTAS/Patcher/Utils/DebugHelp.cs b/UniTAS/Patcher/Utils/DebugHelp.cs index 440b2eac4..768ec57d1 100644 --- a/UniTAS/Patcher/Utils/DebugHelp.cs +++ b/UniTAS/Patcher/Utils/DebugHelp.cs @@ -18,16 +18,18 @@ public static string PrintClass(object obj) private static string PrintClass(object obj, ref int indent, List foundReferences, bool ignoreInitialIndent = false) { + var initialIndent = $"{(ignoreInitialIndent ? "" : IndentString(indent))}"; + if (obj == null) { - return $"{(ignoreInitialIndent ? "" : IndentString(indent))}null"; + return $"{initialIndent}null"; } var type = obj.GetType(); - var str = $"{(ignoreInitialIndent ? "" : IndentString(indent))}{type.Name} {{\n"; + var str = $"{initialIndent}{type.Name} {{\n"; indent++; - if (type.IsClass) + if (type.IsClass && type != typeof(string)) { foundReferences.Add(obj); } @@ -60,6 +62,11 @@ private static string PrintClass(object obj, ref int indent, List foundR var fieldType = field.FieldType; + if (fieldType.IsClass && fieldType != typeof(string)) + { + foundReferences.Add(value); + } + // direct use cases if (fieldType.IsPointer) { From 2ef5c9383613bdd96e73a280170cfce99d33d348 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 17 Nov 2023 00:29:12 +0000 Subject: [PATCH 113/116] changed circular reference detection --- UniTAS/Patcher/Utils/DebugHelp.cs | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/UniTAS/Patcher/Utils/DebugHelp.cs b/UniTAS/Patcher/Utils/DebugHelp.cs index 768ec57d1..9ae169d33 100644 --- a/UniTAS/Patcher/Utils/DebugHelp.cs +++ b/UniTAS/Patcher/Utils/DebugHelp.cs @@ -25,6 +25,12 @@ private static string PrintClass(object obj, ref int indent, List foundR return $"{initialIndent}null"; } + // circular reference + if (foundReferences.Contains(obj)) + { + return $"{initialIndent}..."; + } + var type = obj.GetType(); var str = $"{initialIndent}{type.Name} {{\n"; indent++; @@ -47,13 +53,6 @@ private static string PrintClass(object obj, ref int indent, List foundR var value = field.GetValue(obj); - // circular reference - if (foundReferences.Contains(value)) - { - str += "...,\n"; - continue; - } - if (value is null) { str += "null,\n"; @@ -62,11 +61,6 @@ private static string PrintClass(object obj, ref int indent, List foundR var fieldType = field.FieldType; - if (fieldType.IsClass && fieldType != typeof(string)) - { - foundReferences.Add(value); - } - // direct use cases if (fieldType.IsPointer) { From e5290c72d3559fd889bf8bb41b908038da137d88 Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 17 Nov 2023 01:56:06 +0000 Subject: [PATCH 114/116] added basic backup font search and set for IMGUI --- .../Implementations/IMGUIFontBackupSet.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 UniTAS/Patcher/Implementations/IMGUIFontBackupSet.cs diff --git a/UniTAS/Patcher/Implementations/IMGUIFontBackupSet.cs b/UniTAS/Patcher/Implementations/IMGUIFontBackupSet.cs new file mode 100644 index 000000000..fc899a1ba --- /dev/null +++ b/UniTAS/Patcher/Implementations/IMGUIFontBackupSet.cs @@ -0,0 +1,51 @@ +using System.Linq; +using UniTAS.Patcher.Interfaces.DependencyInjection; +using UniTAS.Patcher.Services.Logging; +using UniTAS.Patcher.Services.UnityEvents; +using UniTAS.Patcher.Utils; +using UnityEngine; + +namespace UniTAS.Patcher.Implementations; + +[Singleton] +[ForceInstantiate] +[ExcludeRegisterIfTesting] +public class IMGUIFontBackupSet +{ + private readonly IUpdateEvents _updateEvents; + private readonly Font _font; + private readonly ILogger _logger; + + public IMGUIFontBackupSet(ILogger logger, IUpdateEvents updateEvents) + { + _logger = logger; + _updateEvents = updateEvents; + var fonts = ResourcesUtils.FindObjectsOfTypeAll(); + if (fonts.Length == 0) + { + logger.LogWarning("no fallback font found for unity IMGUI"); + return; + } + + foreach (var font in fonts) + { + _logger.LogDebug($"found font: {string.Join(", ", font.fontNames)}"); + } + + _font = fonts.FirstOrDefault(x => x.fontNames.Contains("Liberation Sans")); + if (_font == null) + { + logger.LogWarning("no fallback font found for unity IMGUI"); + return; + } + + _updateEvents.OnGUIUnconditional += OnGUIUnconditional; + } + + private void OnGUIUnconditional() + { + _updateEvents.OnGUIUnconditional -= OnGUIUnconditional; + UnityEngine.GUI.skin.font = _font; + _logger.LogInfo($"Set unity IMGUI font to {_font.name}"); + } +} \ No newline at end of file From e484f27c9e4520a561cc26f55a23502b3c07a0de Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 17 Nov 2023 01:58:59 +0000 Subject: [PATCH 115/116] don't hook last resort --- UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs b/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs index 699c2d353..739629a69 100644 --- a/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs +++ b/UniTAS/Patcher/Patches/Preloader/UnityInitInvoke.cs @@ -64,7 +64,8 @@ public override void Patch(ref AssemblyDefinition assembly) { TryHookAwakes(assembly); // TryHookRuntimeInits(assembly); - TryHookLastResort(assembly); + // this probably interferes with actual loading since it could initialise too early + // TryHookLastResort(assembly); } // doing this seems to fail so Awake hooks are enough From 7322a87e593f0c0c275461bd476b453ba3bccfae Mon Sep 17 00:00:00 2001 From: Eddio0141 Date: Fri, 17 Nov 2023 01:59:46 +0000 Subject: [PATCH 116/116] updated target assemblies --- UniTAS/Patcher/Utils/TargetPatcherDlls.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs index bcf176407..1fc2518d8 100644 --- a/UniTAS/Patcher/Utils/TargetPatcherDlls.cs +++ b/UniTAS/Patcher/Utils/TargetPatcherDlls.cs @@ -38,7 +38,12 @@ public static IEnumerable AllExcludedDLLs "netstandard", "mscorlib", "Mono.*", - "Mono" + "Mono", + // no need + "Newtonsoft.Json", + + // should be fine + "UnityEngine.IMGUIModule", }; private static string[] AssemblyIncludeRaw { get; } =