Files
2026-10-16 23:33:13 -07:00

101 lines
3.5 KiB
C#

using System;
using System.Collections.Generic;
using System.Reflection;
using HarmonyLib;
using UnityEngine;
/// <summary>
/// Drops known-noise log lines inside Unity's own logger, so they never reach the
/// console or BepInEx's LogOutput.log. The game funnels both Debug.LogError and
/// Debug.LogWarning through Engine.Logger.Log(LogType, object) - that is the
/// single place where a message can be refused before anyone formats it.
/// </summary>
internal static class LogNoiseFilter
{
private const string SelfTestProbe =
"The character used for Underline is not available in font asset [radicade-filter-self-test].";
internal static void Apply(Harmony harmony)
{
var prefix = AccessTools.Method(typeof(LogNoiseFilter), nameof(Prefix));
if (prefix == null)
{
Diag.Warn("[LogFilter] the filter method was not found; known-noise log spam stays as-is.");
return;
}
var patched = 0;
foreach (var target in Targets())
{
try
{
harmony.Patch(target, prefix: new HarmonyMethod(prefix));
patched++;
}
catch (Exception ex)
{
Diag.Warn("[LogFilter] could not patch " + target.DeclaringType?.Name + "." + target.Name +
" (" + ex.GetType().Name + ": " + ex.Message + ").");
}
}
if (patched == 0)
{
Diag.Warn("[LogFilter] no Unity log funnel could be patched; the RigidbodyEx and font-underline spam stays as-is.");
return;
}
Diag.Info("[LogFilter] armed on " + patched + " Unity log funnel(s): " + LogNoiseRules.PatternCount +
" known-noise pattern(s) are dropped before they are printed.");
SelfTest();
}
private static IEnumerable<MethodBase> Targets()
{
var log = AccessTools.Method(typeof(Logger), "Log",
new[] { typeof(LogType), typeof(Il2CppSystem.Object) });
if (log != null) yield return log;
var logWithContext = AccessTools.Method(typeof(Logger), "Log",
new[] { typeof(LogType), typeof(Il2CppSystem.Object), typeof(UnityEngine.Object) });
if (logWithContext != null) yield return logWithContext;
}
private static bool Prefix(object[] __args)
{
try
{
if (__args == null || __args.Length < 2) return true;
var message = __args[1];
if (message == null) return true;
return !LogNoiseRules.ShouldDrop(message.ToString());
}
catch
{
return true;
}
}
/// <summary>
/// Fires one probe line through Debug.LogWarning and checks that the rules saw
/// it. If they did not, the filter is not wired to this build's logger and the
/// log says so instead of pretending the spam is gone.
/// </summary>
private static void SelfTest()
{
try
{
var before = LogNoiseRules.DroppedCount;
UnityEngine.Debug.LogWarning(SelfTestProbe);
if (LogNoiseRules.DroppedCount > before)
Diag.Info("[LogFilter] self-test PASS: a probe log line was intercepted inside Unity's logger.");
else
Diag.Warn("[LogFilter] self-test FAIL: a probe log line reached the log, so this build's spam is not being filtered.");
}
catch (Exception ex)
{
Diag.Warn("[LogFilter] self-test could not run (" + ex.GetType().Name + ": " + ex.Message + ").");
}
}
}