From 0024a0b810be5834e133a0a09740bee7c88c9b07 Mon Sep 17 00:00:00 2001 From: Nytra <14206961+Nytra@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:42:54 +0000 Subject: [PATCH 1/7] Fixes for headless --- BepInExResoniteShim.cs | 19 ++++++++++++++----- LogAlerter.cs | 18 +++++++++++++++--- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/BepInExResoniteShim.cs b/BepInExResoniteShim.cs index 96bdb78..dcf9469 100644 --- a/BepInExResoniteShim.cs +++ b/BepInExResoniteShim.cs @@ -66,11 +66,20 @@ public override void Load() void RunPatches() { - HarmonyInstance.PatchAllUncategorized();//core patches. if these fail everything fails. - HarmonyInstance.SafePatchCategory(nameof(GraphicalClientPatch)); - HarmonyInstance.SafePatchCategory(nameof(WindowTitlePatcher)); - HarmonyInstance.SafePatchCategory(nameof(LogAlerter)); - HarmonyInstance.SafePatchCategory(nameof(RelativePathFixer)); + if (!Directory.GetCurrentDirectory().EndsWith("Headless")) + { + HarmonyInstance.PatchAllUncategorized();//core patches. if these fail everything fails. + HarmonyInstance.SafePatchCategory(nameof(GraphicalClientPatch)); + HarmonyInstance.SafePatchCategory(nameof(WindowTitlePatcher)); + HarmonyInstance.SafePatchCategory(nameof(RelativePathFixer)); + HarmonyInstance.SafePatchCategory(nameof(LogAlerter)); + } + else + { + HarmonyInstance.PatchAll(typeof(LocationFixer)); + HarmonyInstance.PatchAll(typeof(AssemblyLoadFixer)); + HarmonyInstance.PatchAll(typeof(LogAlerter)); + } } [HarmonyPatch] diff --git a/LogAlerter.cs b/LogAlerter.cs index 14facf7..ecb2cc2 100644 --- a/LogAlerter.cs +++ b/LogAlerter.cs @@ -1,4 +1,5 @@ -using Elements.Core; +using System.Reflection; +using Elements.Core; using HarmonyLib; namespace BepInExResoniteShim; @@ -9,9 +10,20 @@ namespace BepInExResoniteShim; [HarmonyPatch(typeof(UniLog), "add_OnLog")] class LogAlerter { + static Type _headlessType = AccessTools.TypeByName("FrooxEngine.Headless.Program"); + static FieldInfo _logStreamField = AccessTools.Field(_headlessType, "logStream"); static void Postfix(Action value) { - if(AnyPatchFailed) value($"[BepisLoader] BepInExResoniteShim partially loaded."); - else value($"[BepisLoader] BepInExResoniteShim loaded successfully."); + Task.Run(async () => + { + if (_headlessType is not null) + { + while (_logStreamField.GetValue(null) is null) + await Task.Delay(1); + } + + if(AnyPatchFailed) value($"[BepisLoader] BepInExResoniteShim partially loaded."); + else value($"[BepisLoader] BepInExResoniteShim loaded successfully."); + }); } } From d4ff67c2f0f39d0aedb8eec7fffdbdda78f63356 Mon Sep 17 00:00:00 2001 From: hazre <37149950+hazre@users.noreply.github.com> Date: Tue, 20 Jan 2026 18:01:15 +0100 Subject: [PATCH 2/7] Improve headless detection using Harmony AccessTools --- BepInExResoniteShim.cs | 16 +++++++++------- LogAlerter.cs | 6 +++--- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/BepInExResoniteShim.cs b/BepInExResoniteShim.cs index dcf9469..55b0204 100644 --- a/BepInExResoniteShim.cs +++ b/BepInExResoniteShim.cs @@ -28,6 +28,8 @@ class BepInExResoniteShim : BasePlugin { internal static new ManualLogSource Log = null!; static ConfigEntry ShowWatermark = null!; + internal static readonly Type? HeadlessType = AccessTools.TypeByName("FrooxEngine.Headless.Program"); + public static bool IsHeadless => HeadlessType != null; public override void Load() { @@ -66,7 +68,13 @@ public override void Load() void RunPatches() { - if (!Directory.GetCurrentDirectory().EndsWith("Headless")) + if (IsHeadless) + { + HarmonyInstance.PatchAll(typeof(LocationFixer)); + HarmonyInstance.PatchAll(typeof(AssemblyLoadFixer)); + HarmonyInstance.PatchAll(typeof(LogAlerter)); + } + else { HarmonyInstance.PatchAllUncategorized();//core patches. if these fail everything fails. HarmonyInstance.SafePatchCategory(nameof(GraphicalClientPatch)); @@ -74,12 +82,6 @@ void RunPatches() HarmonyInstance.SafePatchCategory(nameof(RelativePathFixer)); HarmonyInstance.SafePatchCategory(nameof(LogAlerter)); } - else - { - HarmonyInstance.PatchAll(typeof(LocationFixer)); - HarmonyInstance.PatchAll(typeof(AssemblyLoadFixer)); - HarmonyInstance.PatchAll(typeof(LogAlerter)); - } } [HarmonyPatch] diff --git a/LogAlerter.cs b/LogAlerter.cs index ecb2cc2..f1b8674 100644 --- a/LogAlerter.cs +++ b/LogAlerter.cs @@ -10,13 +10,13 @@ namespace BepInExResoniteShim; [HarmonyPatch(typeof(UniLog), "add_OnLog")] class LogAlerter { - static Type _headlessType = AccessTools.TypeByName("FrooxEngine.Headless.Program"); - static FieldInfo _logStreamField = AccessTools.Field(_headlessType, "logStream"); + static readonly FieldInfo? _logStreamField = AccessTools.Field(BepInExResoniteShim.HeadlessType, "logStream"); + static void Postfix(Action value) { Task.Run(async () => { - if (_headlessType is not null) + if (BepInExResoniteShim.IsHeadless && _logStreamField != null) { while (_logStreamField.GetValue(null) is null) await Task.Delay(1); From b78733c77156fc6ae5f1d5d29c28afafeb77c8b1 Mon Sep 17 00:00:00 2001 From: hazre <37149950+hazre@users.noreply.github.com> Date: Tue, 20 Jan 2026 18:43:30 +0100 Subject: [PATCH 3/7] Simplify RunPatches to always run core patches --- BepInExResoniteShim.cs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/BepInExResoniteShim.cs b/BepInExResoniteShim.cs index 55b0204..6315c48 100644 --- a/BepInExResoniteShim.cs +++ b/BepInExResoniteShim.cs @@ -50,7 +50,7 @@ public override void Load() ConvertToObject = (str, type) => typeof(Coder<>).MakeGenericType(type).GetMethod("DecodeFromString")!.Invoke(null, [str])!, }); } - + lastAttempted = typeof(dummy); TomlTypeConverter.AddConverter(typeof(dummy), new TypeConverter { @@ -68,19 +68,17 @@ public override void Load() void RunPatches() { - if (IsHeadless) - { - HarmonyInstance.PatchAll(typeof(LocationFixer)); - HarmonyInstance.PatchAll(typeof(AssemblyLoadFixer)); - HarmonyInstance.PatchAll(typeof(LogAlerter)); - } - else + // explicit types to avoid assembly scanning which crashes headless + HarmonyInstance.PatchAll(typeof(LocationFixer)); + HarmonyInstance.PatchAll(typeof(AssemblyLoadFixer)); + HarmonyInstance.PatchAll(typeof(LogAlerter)); + + // Graphical-only patches + if (!IsHeadless) { - HarmonyInstance.PatchAllUncategorized();//core patches. if these fail everything fails. HarmonyInstance.SafePatchCategory(nameof(GraphicalClientPatch)); HarmonyInstance.SafePatchCategory(nameof(WindowTitlePatcher)); HarmonyInstance.SafePatchCategory(nameof(RelativePathFixer)); - HarmonyInstance.SafePatchCategory(nameof(LogAlerter)); } } @@ -212,7 +210,7 @@ public static void Prefix(RendererInitData __instance) static class HarmonyExtensions { - public static bool AnyPatchFailed { get; private set; } + public static bool AnyPatchFailed { get; private set; } public static void SafePatchCategory(this Harmony instance, string categoryName) { try From 298ea05be4fa7f8df04f3f4a1ebb39a6723c722b Mon Sep 17 00:00:00 2001 From: hazre <37149950+hazre@users.noreply.github.com> Date: Tue, 20 Jan 2026 18:43:33 +0100 Subject: [PATCH 4/7] Add timeout to LogAlerter headless wait loop --- LogAlerter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/LogAlerter.cs b/LogAlerter.cs index f1b8674..708408c 100644 --- a/LogAlerter.cs +++ b/LogAlerter.cs @@ -18,7 +18,8 @@ static void Postfix(Action value) { if (BepInExResoniteShim.IsHeadless && _logStreamField != null) { - while (_logStreamField.GetValue(null) is null) + var timeout = DateTime.UtcNow.AddSeconds(10); + while (_logStreamField.GetValue(null) is null && DateTime.UtcNow < timeout) await Task.Delay(1); } From 3460291f1ad78f22799332cef6e6c0a066722e25 Mon Sep 17 00:00:00 2001 From: hazre <37149950+hazre@users.noreply.github.com> Date: Tue, 20 Jan 2026 20:59:40 +0100 Subject: [PATCH 5/7] Fix headless type detection using fully qualified assembly name --- BepInExResoniteShim.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BepInExResoniteShim.cs b/BepInExResoniteShim.cs index 6315c48..d349b93 100644 --- a/BepInExResoniteShim.cs +++ b/BepInExResoniteShim.cs @@ -28,7 +28,7 @@ class BepInExResoniteShim : BasePlugin { internal static new ManualLogSource Log = null!; static ConfigEntry ShowWatermark = null!; - internal static readonly Type? HeadlessType = AccessTools.TypeByName("FrooxEngine.Headless.Program"); + internal static readonly Type? HeadlessType = Type.GetType("FrooxEngine.Headless.Program, Resonite"); public static bool IsHeadless => HeadlessType != null; public override void Load() From 58a6f58bc2739c87cb95ba07eb9b792edd95eb3f Mon Sep 17 00:00:00 2001 From: hazre <37149950+hazre@users.noreply.github.com> Date: Tue, 20 Jan 2026 22:08:12 +0100 Subject: [PATCH 6/7] Simplify patch loading with SafePatchAll --- BepInExResoniteShim.cs | 43 ++++++++++++++------------------------- GraphicalClientPatches.cs | 1 - LogAlerter.cs | 9 ++++---- RelativePathFixer.cs | 2 -- 4 files changed, 20 insertions(+), 35 deletions(-) diff --git a/BepInExResoniteShim.cs b/BepInExResoniteShim.cs index d349b93..a22d45f 100644 --- a/BepInExResoniteShim.cs +++ b/BepInExResoniteShim.cs @@ -28,8 +28,6 @@ class BepInExResoniteShim : BasePlugin { internal static new ManualLogSource Log = null!; static ConfigEntry ShowWatermark = null!; - internal static readonly Type? HeadlessType = Type.GetType("FrooxEngine.Headless.Program, Resonite"); - public static bool IsHeadless => HeadlessType != null; public override void Load() { @@ -63,23 +61,8 @@ public override void Load() Log.LogError($"Failed to register generic type converters (Last attempted = {lastAttempted?.ToString() ?? "NULL"}): " + e); } - RunPatches(); - } - - void RunPatches() - { - // explicit types to avoid assembly scanning which crashes headless - HarmonyInstance.PatchAll(typeof(LocationFixer)); - HarmonyInstance.PatchAll(typeof(AssemblyLoadFixer)); - HarmonyInstance.PatchAll(typeof(LogAlerter)); - - // Graphical-only patches - if (!IsHeadless) - { - HarmonyInstance.SafePatchCategory(nameof(GraphicalClientPatch)); - HarmonyInstance.SafePatchCategory(nameof(WindowTitlePatcher)); - HarmonyInstance.SafePatchCategory(nameof(RelativePathFixer)); - } + // Apply all patches, incompatible patches are skipped gracefully + HarmonyInstance.SafePatchAll(); } [HarmonyPatch] @@ -162,7 +145,6 @@ public static IEnumerable Transpiler(IEnumerable value) { Task.Run(async () => { - if (BepInExResoniteShim.IsHeadless && _logStreamField != null) + if (_logStreamField != null) { var timeout = DateTime.UtcNow.AddSeconds(10); while (_logStreamField.GetValue(null) is null && DateTime.UtcNow < timeout) await Task.Delay(1); } - if(AnyPatchFailed) value($"[BepisLoader] BepInExResoniteShim partially loaded."); + if (AnyPatchFailed) value($"[BepisLoader] BepInExResoniteShim partially loaded."); else value($"[BepisLoader] BepInExResoniteShim loaded successfully."); }); } diff --git a/RelativePathFixer.cs b/RelativePathFixer.cs index 726877a..bfb8e15 100644 --- a/RelativePathFixer.cs +++ b/RelativePathFixer.cs @@ -8,7 +8,6 @@ namespace BepInExResoniteShim; class RelativePathFixer { - [HarmonyPatchCategory(nameof(RelativePathFixer))] [HarmonyPatch(typeof(Program), "
$", MethodType.Async)] class RenderiteHostPathFixes { @@ -45,7 +44,6 @@ public static void FileWriteInjected(string path, string? contents) } } - [HarmonyPatchCategory(nameof(RelativePathFixer))] [HarmonyPatch(typeof(RenderSystem), "StartRenderer", MethodType.Async)] public class RenderiteWorkingDirectoryFix { From 108e43d667bedce5d9bd90ca9a5f15e1555c77ac Mon Sep 17 00:00:00 2001 From: hazre <37149950+hazre@users.noreply.github.com> Date: Tue, 20 Jan 2026 22:55:22 +0100 Subject: [PATCH 7/7] Refactor SafePatchAll into RunPatches method Move patch logic from HarmonyExtensions class into the main BepInExResoniteShim class to avoid confusion with HarmonyLib methods. --- BepInExResoniteShim.cs | 47 +++++++++++++++++++++--------------------- LogAlerter.cs | 5 +---- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/BepInExResoniteShim.cs b/BepInExResoniteShim.cs index fe67525..e8a7e9a 100644 --- a/BepInExResoniteShim.cs +++ b/BepInExResoniteShim.cs @@ -90,8 +90,29 @@ public override void Load() Log.LogError($"Failed to register generic type converters (Last attempted = {lastAttempted?.ToString() ?? "NULL"}): " + e); } - // Apply all patches, incompatible patches are skipped gracefully - HarmonyInstance.SafePatchAll(); + RunPatches(HarmonyInstance); + } + + internal static bool AnyPatchFailed { get; private set; } + + /// + /// Apply all patches, incompatible patches are skipped gracefully. + /// + static void RunPatches(Harmony harmony) + { + var assembly = Assembly.GetExecutingAssembly(); + foreach (var type in AccessTools.GetTypesFromAssembly(assembly)) + { + try + { + harmony.CreateClassProcessor(type).Patch(); + } + catch (Exception e) + { + Log.LogDebug($"Skipped patching {type.Name}: {e.Message}"); + AnyPatchFailed = true; + } + } } [HarmonyPatch] @@ -194,25 +215,3 @@ public static void Prefix(RendererInitData __instance) } } } - -static class HarmonyExtensions -{ - public static bool AnyPatchFailed { get; private set; } - - public static void SafePatchAll(this Harmony instance, Assembly? assembly = null) - { - assembly ??= Assembly.GetCallingAssembly(); - foreach (var type in AccessTools.GetTypesFromAssembly(assembly)) - { - try - { - instance.CreateClassProcessor(type).Patch(); - } - catch (Exception e) - { - BepInExResoniteShim.Log.LogDebug($"Skipped patching {type.Name}: {e.Message}"); - AnyPatchFailed = true; - } - } - } -} diff --git a/LogAlerter.cs b/LogAlerter.cs index 05d50a1..136e965 100644 --- a/LogAlerter.cs +++ b/LogAlerter.cs @@ -4,9 +4,6 @@ namespace BepInExResoniteShim; -using static HarmonyExtensions; - - [HarmonyPatch(typeof(UniLog), "add_OnLog")] class LogAlerter { @@ -24,7 +21,7 @@ static void Postfix(Action value) await Task.Delay(1); } - if (AnyPatchFailed) value($"[BepisLoader] BepInExResoniteShim partially loaded."); + if (BepInExResoniteShim.AnyPatchFailed) value($"[BepisLoader] BepInExResoniteShim partially loaded."); else value($"[BepisLoader] BepInExResoniteShim loaded successfully."); }); }