mirror of
https://github.com/BepInEx/BepInEx.git
synced 2026-10-25 12:46:44 +00:00
UnityLogSource's static constructor falls back to Application.RegisterLogCallback when Application.logMessageReceived is absent. Unity's managed code stripper can remove both, in which case GetMethod returns null and the unguarded Invoke throws a NullReferenceException out of the static constructor. That exception propagates through chainloader initialisation, so the chainloader never starts and no plugins load. The failure is close to undiagnosable: LogOutput.log ends at "Preloader finished" with no error, and the exception only appears on stdout. Unity log forwarding is optional, so warn and continue instead of taking the whole loader down. Seen on Beacon Pines (Unity 2021.3.45f1, Mono), whose UnityEngine.Application has neither member.
115 lines
3.7 KiB
C#
115 lines
3.7 KiB
C#
using System;
|
|
using System.Reflection;
|
|
using BepInEx.Logging;
|
|
using UnityEngine;
|
|
using Logger = BepInEx.Logging.Logger;
|
|
|
|
namespace BepInEx.Unity.Mono.Logging;
|
|
|
|
/// <summary>
|
|
/// Logs entries using Unity specific outputs.
|
|
/// </summary>
|
|
public class UnityLogSource : ILogSource
|
|
{
|
|
private bool disposed;
|
|
|
|
/// <summary>
|
|
/// Creates a new Unity log source.
|
|
/// </summary>
|
|
public UnityLogSource()
|
|
{
|
|
InternalUnityLogMessage += UnityLogMessageHandler;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public string SourceName { get; } = "Unity Log";
|
|
|
|
/// <inheritdoc />
|
|
public event EventHandler<LogEventArgs> LogEvent;
|
|
|
|
/// <inheritdoc />
|
|
public void Dispose()
|
|
{
|
|
if (!disposed)
|
|
{
|
|
InternalUnityLogMessage -= UnityLogMessageHandler;
|
|
disposed = true;
|
|
}
|
|
}
|
|
|
|
private void UnityLogMessageHandler(object sender, LogEventArgs eventArgs)
|
|
{
|
|
var newEventArgs = new LogEventArgs(eventArgs.Data, eventArgs.Level, this);
|
|
LogEvent?.Invoke(this, newEventArgs);
|
|
}
|
|
|
|
#region Static Unity handler
|
|
|
|
private static event EventHandler<LogEventArgs> InternalUnityLogMessage;
|
|
|
|
static UnityLogSource()
|
|
{
|
|
var callback = new Application.LogCallback(OnUnityLogMessageReceived);
|
|
|
|
var logEvent =
|
|
typeof(Application).GetEvent("logMessageReceived", BindingFlags.Public | BindingFlags.Static);
|
|
if (logEvent != null)
|
|
{
|
|
logEvent.AddEventHandler(null, callback);
|
|
//UnsubscribeAction = () => logEvent.RemoveEventHandler(null, callback);
|
|
}
|
|
else
|
|
{
|
|
var registerLogCallback =
|
|
typeof(Application).GetMethod("RegisterLogCallback", BindingFlags.Public | BindingFlags.Static);
|
|
|
|
// Unity's managed code stripper can remove both Application.logMessageReceived and the
|
|
// legacy Application.RegisterLogCallback. Invoking a null MethodInfo here throws a
|
|
// NullReferenceException out of this static constructor, which propagates through
|
|
// Chainloader.Initialize and silently prevents the chainloader (and therefore every
|
|
// plugin) from starting. Unity log forwarding is optional, so degrade instead of
|
|
// taking the whole loader down with us.
|
|
if (registerLogCallback == null)
|
|
{
|
|
Logger.Log(LogLevel.Warning,
|
|
"Unity log forwarding is unavailable: neither " +
|
|
"Application.logMessageReceived nor Application.RegisterLogCallback exist " +
|
|
"in this build (they were most likely stripped). Unity log messages will " +
|
|
"not appear in the BepInEx log.");
|
|
return;
|
|
}
|
|
|
|
registerLogCallback.Invoke(null, new object[] { callback });
|
|
//UnsubscribeAction = () => registerLogCallback.Invoke(null, new object[] { null });
|
|
}
|
|
}
|
|
|
|
private static void OnUnityLogMessageReceived(string message, string stackTrace, LogType type)
|
|
{
|
|
LogLevel logLevel;
|
|
|
|
switch (type)
|
|
{
|
|
case LogType.Error:
|
|
case LogType.Assert:
|
|
case LogType.Exception:
|
|
logLevel = LogLevel.Error;
|
|
break;
|
|
case LogType.Warning:
|
|
logLevel = LogLevel.Warning;
|
|
break;
|
|
case LogType.Log:
|
|
default:
|
|
logLevel = LogLevel.Info;
|
|
break;
|
|
}
|
|
|
|
if (type == LogType.Exception)
|
|
message += $"\nStack trace:\n{stackTrace}";
|
|
|
|
InternalUnityLogMessage?.Invoke(null, new LogEventArgs(message, logLevel, null));
|
|
}
|
|
|
|
#endregion
|
|
}
|