Degrade instead of crashing when the IL2CPP interop assemblies are missing (#1338)

* Degrade instead of crashing when the interop assemblies are missing

OnInvokeMethod references interop types (UnityEngine.Application/LogType) in its body. The CLR JIT-compiles the whole method before running any of it, so when the interop assemblies cannot be resolved the method throws FileNotFoundException at JIT time -- before its own try is entered -- and the exception escapes into the native runtime-invoke detour and crashes the process with nothing logged.

Move the interop-referencing call into its own SetupUnityLogging method called inside the try. OnInvokeMethod then JITs without the interop assemblies present, the missing-interop failure is caught, and the loader degrades (logs and continues) so the game runs unmodded. Set unhook at the top of the try so the detour fires exactly once even on failure.

* Condense the interop-degrade comments and reword the fatal log

Per review: trim the JIT-bypass comments to one line each and change
the fatal message to "Unable to execute IL2CPP chainloader, no plugins
will be loaded".
This commit is contained in:
HetCreep authored and GitHub committed 2026-06-11 11:54:22 +02:00
1 parent a1afbfb169
commit a06688e85b
1 file changed
+16 -9
@@ -83,23 +83,19 @@ public class IL2CPPChainloader : BaseChainloader<BasePlugin>
if (methodName == "Internal_ActiveSceneChanged")
try
{
if (ConfigUnityLogging.Value)
{
Logger.Sources.Add(new IL2CPPUnityLogSource());
Application.CallLogCallback("Test call after applying unity logging hook", "", LogType.Assert,
true);
}
// Unhook up front so the detour fires once even if setup below throws.
unhook = true;
// Isolated so a missing-interop JIT failure happens inside the try (caught), not in OnInvokeMethod itself.
SetupUnityLogging();
Il2CppInteropManager.PreloadInteropAssemblies();
Instance.Execute();
}
catch (Exception ex)
{
Logger.Log(LogLevel.Fatal, "Unable to execute IL2CPP chainloader");
Logger.Log(LogLevel.Fatal, "Unable to execute IL2CPP chainloader, no plugins will be loaded");
Logger.Log(LogLevel.Error, ex);
}
@@ -115,6 +111,17 @@ public class IL2CPPChainloader : BaseChainloader<BasePlugin>
return result;
}
// JIT-compiled only when called here, so OnInvokeMethod needs no interop assemblies present to JIT.
private static void SetupUnityLogging()
{
if (!ConfigUnityLogging.Value)
return;
Logger.Sources.Add(new IL2CPPUnityLogSource());
Application.CallLogCallback("Test call after applying unity logging hook", "", LogType.Assert, true);
}
protected override void InitializeLoggers()
{
base.InitializeLoggers();